了解 Aspects 框架

3,969 阅读9分钟

前一段时间看了一篇文章(字节团队出的)使用 method_swizzle + NSProxy 解决 App 全埋点的问题。

然后了解了一下 NSProxy 这个虚基类,发现平常开发中能使用 NSProxy 解决定时器循环引用问题、解决 OC 的多继承问题、也能让框架有更好的扩展性。

立即打开工程内的 YYWeakProxy 看了下,发现是用消息转发实现的。在我核桃大的脑袋里,对于消息转发还停留在解决 App 内方法未实现时崩溃的拦截。

立即搜索 消息转发 的用途,发现了两个好玩的框架:

ps: 都是老框架了,不妨碍学习。

本文就是对 Aspects 框架的学习。

1、示例 Demo :

下方示例来自 Aspects Demo :

// ViewController.m

@interface** ViewController ()

@end

@implementation** ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    UIViewController *testController = [[UIImagePickerController alloc] init];
    testController.modalPresentationStyle = UIModalPresentationFormSheet;
    [self presentViewController:testController animated:YES completion:NULL];
    
    [testController aspect_hookSelector: @selector(viewWillDisappear:) withOptions:AspectPositionAfter usingBlock:^(id<AspectInfo> info, BOOL animated) {
        NSLog(@"Popped Hello from Aspects", [info instance]);
    } error:NULL];

}

2、方法列表

/**
 Aspects uses Objective-C message forwarding to hook into messages. This will create some overhead. Don't add aspects to methods that are called a lot. Aspects is meant for view/controller code that is not called a 1000 times per second.

 Adding aspects returns an opaque token which can be used to deregister again. All calls are thread safe.
 */
@interface NSObject (Aspects)

/// Adds a block of code before/instead/after the current `selector` for a specific class.
///
/// @param block Aspects replicates the type signature of the method being hooked.
/// The first parameter will be `id<AspectInfo>`, followed by all parameters of the method.
/// These parameters are optional and will be filled to match the block signature.
/// You can even use an empty block, or one that simple gets `id<AspectInfo>`.
///
/// @note Hooking static methods is not supported.
/// @return A token which allows to later deregister the aspect.
+ (id<AspectToken>)aspect_hookSelector:(SEL)selector
                      withOptions:(AspectOptions)options
                       usingBlock:(id)block
                            error:(NSError **)error;

/// Adds a block of code before/instead/after the current `selector` for a specific instance.
- (id<AspectToken>)aspect_hookSelector:(SEL)selector
                      withOptions:(AspectOptions)options
                       usingBlock:(id)block
                            error:(NSError **)error;

@end

AspectsNSObject 的分类,只有两个方法,一个类方法,一个实例方法,使用Demo中的代码研究实例方法的调用。

3、调用流程

image.png

描述了调用主的流程,关于销毁的流程没有列入,本文不做研究。

4、代码分析

接下来对代码分析,大部分理解在代码中都注释好了。

1、入口方法 aspect_add

static id aspect_add(id self, SEL selector, AspectOptions options, id block, NSError **error) {
    NSCParameterAssert(self);
    NSCParameterAssert(selector);
    NSCParameterAssert(block);

    __block AspectIdentifier *identifier = nil;
    
    /// 加锁 SpinLockLock (名为自旋锁,其实iOS 7 已经改成了 os_unfair_lock 互斥锁)
    aspect_performLocked(^{
        if (aspect_isSelectorAllowedAndTrack(self, selector, options, error)) {
            // 加载或创建 aspect 容器。
            AspectsContainer *aspectContainer = aspect_getContainerForObject(self, selector);
            // 创建 AspectIdentifier 对象
            identifier = [AspectIdentifier identifierWithSelector:selector object:self options:options block:block error:error];
            if (identifier) {
                // 按 options 分类存储 AspectIdentifier 的对象
                [aspectContainer addAspect:identifier withOptions:options];

                // Modify the class to allow message interception.
                // 修改该类以允许消息拦截。
                aspect_prepareClassAndHookSelector(self, selector, error);
            }
        }
    });
    return identifier;
}

2、判断该方法是否能 hook aspect_isSelectorAllowedAndTrack

当前方法中过滤了 retain,release,autorelease, forwardInvocation: 不能 hook 的方法,并且 dealloc 方法只能在销毁前 hook。

static BOOL aspect_isSelectorAllowedAndTrack(NSObject *self, SEL selector, AspectOptions options, NSError **error) {
    static NSSet *disallowedSelectorList;
    static dispatch_once_t pred;
    dispatch_once(&pred, ^{
        /// 这些方法不允许 hook
        disallowedSelectorList = [NSSet setWithObjects:@"retain", @"release", @"autorelease", @"forwardInvocation:", nil];
    });

    // Check against the blacklist.
    
    // 如果是前面 disallowedSelectorList 黑名单里面的方法就返回 NO,不允许 hook
    NSString *selectorName = NSStringFromSelector(selector);
    if ([disallowedSelectorList containsObject:selectorName]) {
        NSString *errorDescription = [NSString stringWithFormat:@"Selector %@ is blacklisted.", selectorName];
        AspectError(AspectErrorSelectorBlacklisted, errorDescription);
        return NO;
    }

    // Additional checks.
    
    // 检查 dealloc 方法只能 hook 销毁之前。
    AspectOptions position = options&AspectPositionFilter;
    if ([selectorName isEqualToString:@"dealloc"] && position != AspectPositionBefore) {
        NSString *errorDesc = @"AspectPositionBefore is the only valid position when hooking dealloc.";
        AspectError(AspectErrorSelectorDeallocPosition, errorDesc);
        return NO;
    }

    // 检查方法时候实现,未实现不hook,没意义。
    if (![self respondsToSelector:selector] && ![self.class instancesRespondToSelector:selector]) {
        NSString *errorDesc = [NSString stringWithFormat:@"Unable to find selector -[%@ %@].", NSStringFromClass(self.class), selectorName];
        AspectError(AspectErrorDoesNotRespondToSelector, errorDesc);
        return NO;
    }
    
    // Search for the current class and the class hierarchy IF we are modifying a class object
    // 如果我们正在修改类对象,则搜索当前类和类的层次结构
    if (class_isMetaClass(object_getClass(self))) {
        /// hook 类方法进入
        Class klass = [self class];
        NSMutableDictionary *swizzledClassesDict = aspect_getSwizzledClassesDict();
        Class currentClass = [self class];
        do {
            /// 防止重复 hook
            AspectTracker *tracker = swizzledClassesDict[currentClass];
            if ([tracker.selectorNames containsObject:selectorName]) {

                // Find the topmost class for the log.
                if (tracker.parentEntry) {
                    AspectTracker *topmostEntry = tracker.parentEntry;
                    while (topmostEntry.parentEntry) {
                        topmostEntry = topmostEntry.parentEntry;
                    }
                    NSString *errorDescription = [NSString stringWithFormat:@"Error: %@ already hooked in %@. A method can only be hooked once per class hierarchy.", selectorName, NSStringFromClass(topmostEntry.trackedClass)];
                    AspectError(AspectErrorSelectorAlreadyHookedInClassHierarchy, errorDescription);
                    return NO;
                }else if (klass == currentClass) {
                    // Already modified and topmost!
                    return YES;
                }
            }
        }while ((currentClass = class_getSuperclass(currentClass)));

        // Add the selector as being modified.
        currentClass = klass;
        AspectTracker *parentTracker = nil;
        do {
            /// 依次向父级查找,加入字典
            AspectTracker *tracker = swizzledClassesDict[currentClass];
            if (!tracker) {
                tracker = [[AspectTracker alloc] initWithTrackedClass:currentClass parent:parentTracker];
                swizzledClassesDict[(id<NSCopying>)currentClass] = tracker;
            }
            [tracker.selectorNames addObject:selectorName];
            // All superclasses get marked as having a subclass that is modified.
            parentTracker = tracker;
        }while ((currentClass = class_getSuperclass(currentClass)));
    }

    return YES;
}

3、获取 aspect 容器 aspect_getContainerForObject

// Loads or creates the aspect container.
// 加载或创建 aspect 容器。
static AspectsContainer *aspect_getContainerForObject(NSObject *self, SEL selector) {
    NSCParameterAssert(self);
    
    // 给方法加前缀 aspects_ xx
    SEL aliasSelector = aspect_aliasForSelector(selector);
    
    /// 初始化 AspectsContainer 对象,关联到当前对象上,
    AspectsContainer *aspectContainer = objc_getAssociatedObject(self, aliasSelector);
    if (!aspectContainer) {
        aspectContainer = [AspectsContainer new];
        objc_setAssociatedObject(self, aliasSelector, aspectContainer, OBJC_ASSOCIATION_RETAIN);
    }
    return aspectContainer;
}

4、AspectIdentifier 初始化

这个方法里面,只需要关注 aspect_blockMethodSignature 获取 block 的方法签名。

+ (instancetype)identifierWithSelector:(SEL)selector object:(id)object options:(AspectOptions)options block:(id)block error:(NSError **)error {
    NSCParameterAssert(block);
    NSCParameterAssert(selector);
    
    /// 获取 block 的方法签名
    NSMethodSignature *blockSignature = aspect_blockMethodSignature(block, error); // TODO: check signature compatibility, etc.
    
    /// 验证block 中的参数和 hook 方法的参数 是否兼容
    if (!aspect_isCompatibleBlockSignature(blockSignature, object, selector, error)) {
        return nil;
    }

    /// 构建 identifier 对象
    AspectIdentifier *identifier = nil;
    if (blockSignature) {
        identifier = [AspectIdentifier new];
        identifier.selector = selector;
        identifier.block = block;
        identifier.blockSignature = blockSignature;
        identifier.options = options;
        identifier.object = object; // weak
    }
    return identifier;
}
4.1 获取 block 的方法签名 aspect_blockMethodSignature

这个方法里面,重点关注第一行 AspectBlockRef layout = (__bridge void *)block; ,将 block 桥接了一下,方便取值。

桥接结构如下:

// Block internals.
typedef NS_OPTIONS(int, AspectBlockFlags) {
	AspectBlockFlagsHasCopyDisposeHelpers = (1 << 25),
	AspectBlockFlagsHasSignature          = (1 << 30)
};
typedef struct _AspectBlock {
	__unused Class isa;
	AspectBlockFlags flags;
	__unused int reserved;
	void (__unused *invoke)(struct _AspectBlock *block, ...);
	struct {
		unsigned long int reserved;
		unsigned long int size;
		// requires AspectBlockFlagsHasCopyDisposeHelpers
		void (*copy)(void *dst, const void *src);
		void (*dispose)(const void *);
		// requires AspectBlockFlagsHasSignature
		const char *signature;
		const char *layout;
	} *descriptor;
	// imported variables
} *AspectBlockRef;

源码实现:

/// 获取 block 的方法签名
static NSMethodSignature *aspect_blockMethodSignature(id block, NSError **error) {
    // 因为block 内部构造原因能桥接 block 方便取值。
    // Block 原来你是这样的(一)https://juejin.cn/post/6955409380321787940
    // Block 原来你是这样的(二)https://juejin.cn/post/6956895256998576164

    AspectBlockRef layout = (__bridge void *)block;
    
    // 根据 flags 内部生成规则。判断 BlockFlagsHasSignature  如果没有 block 方法签名就返 nil
	if (!(layout->flags & AspectBlockFlagsHasSignature)) {
        NSString *description = [NSString stringWithFormat:@"The block %@ doesn't contain a type signature.", block];
        AspectError(AspectErrorMissingBlockSignature, description);
        return nil;
    }
    
    /// 取出 descriptor 结构体的首地址
	void *desc = layout->descriptor;
    
    /**
     struct {
         unsigned long int reserved;
         unsigned long int size;
         // requires AspectBlockFlagsHasCopyDisposeHelpers
         void (*copy)(void *dst, const void *src);
         void (*dispose)(const void *);
         // requires AspectBlockFlagsHasSignature
         const char *signature;
         const char *layout;
     } *descriptor;
     */
    
    // 偏移过2个int 单位( 指针走过 unsigned long int reserved; 和 unsigned long int size;)
	desc += 2 * sizeof(unsigned long int);
    
    // 如果 BlockFlagsHasCopyDisposeHelpers 再偏移 2个 void * 单位 ( 指针走过 void (*copy) 和 void (*dispose))
	if (layout->flags & AspectBlockFlagsHasCopyDisposeHelpers) {
		desc += 2 * sizeof(void *);
    }
    
    // 当前指针指向 const char *signature;
    // 如果指针地址不存在。说明 signature 不存在,就返 nil
	if (!desc) {
        NSString *description = [NSString stringWithFormat:@"The block %@ doesn't has a type signature.", block];
        AspectError(AspectErrorMissingBlockSignature, description);
        return nil;
    }
     
    // 返回 NSMethodSignature 对象
	const char *signature = (*(const char **)desc);
	return [NSMethodSignature signatureWithObjCTypes:signature];
}

如果不能理解是如何桥接的,可以看看这两篇文章,对 Block 内部结构的分析。

5、验证 block 中的参数和 hook 方法的参数是否兼容 aspect_isCompatibleBlockSignature

/// 验证block 中的参数和 hook 方法的参数 是否兼容
static BOOL aspect_isCompatibleBlockSignature(NSMethodSignature *blockSignature, id object, SEL selector, NSError **error) {
    NSCParameterAssert(blockSignature);
    NSCParameterAssert(object);
    NSCParameterAssert(selector);

    // 签名匹配
    BOOL signaturesMatch = YES;

    /// 获取需要 hook SEL 的方法签名
    NSMethodSignature *methodSignature = [[object class] instanceMethodSignatureForSelector:selector];
    
    /**
     * 举例: hook 了  viewWillDisappear
     * 方法 ArgumentTypeAtIndex
     *  1 :self
     *  2 :  SEL
     *  3:bool (animated)
     *
     *  blockSignature 中 方法 ArgumentTypeAtIndex
     *  1: self
     *  2: AspectInfo
     *  3: bool (animated)
     *  如果本身 hook 的方法只有 2个 Argument 比如: dealloc (self,SEL)
     *  但是,blockSignature 有 3个 Argument  (self,SEL,bool)
     *  如果这样 blockSignature.numberOfArguments > methodSignature.numberOfArguments 就不匹配。
     *  所以 该方法 允许 blockSignature 少于 methodSignature 的 arguments (有的不使用),但是不能多于。
     */
    if (blockSignature.numberOfArguments > methodSignature.numberOfArguments) {
        signaturesMatch = NO;
    }else {
        if (blockSignature.numberOfArguments > 1) {
            /// 这里需要强制 第二个参数 是 AspectInfo
            const char *blockType = [blockSignature getArgumentTypeAtIndex:1];
            if (blockType[0] != '@') {
                signaturesMatch = NO;
            }
        }
        // Argument 0 is self/block, argument 1 is SEL or id<AspectInfo>. We start comparing at argument 2.
        // The block can have less arguments than the method, that's ok.
        
        // 参数0是self/block,参数1是SEL或id。我们从 index = 2 开始比较。
        // block的参数可以比方法少,这是可以的。
        if (signaturesMatch) {
            for (NSUInteger idx = 2; idx < blockSignature.numberOfArguments; idx++) {
                const char *methodType = [methodSignature getArgumentTypeAtIndex:idx];
                const char *blockType = [blockSignature getArgumentTypeAtIndex:idx];
                // Only compare parameter, not the optional type data.
                
                // 这里允许 blockSignature 少,但是不允许跳过参数,
                // 比如:hook 方法总共 4个参数 (self,SEL,Bool,Int)
                // block 可以 (self,SEL,Bool)不能 (self,SEL,Int)
                if (!methodType || !blockType || methodType[0] != blockType[0]) {
                    signaturesMatch = NO;
                    break;
                }
            }
        }
    }

    /// 如果不匹配打印错误 返回 No
    if (!signaturesMatch) {
        NSString *description = [NSString stringWithFormat:@"Blog signature %@ doesn't match %@.", blockSignature, methodSignature];
        AspectError(AspectErrorIncompatibleBlockSignature, description);
        return NO;
    }
    return YES;
}

看到这里 NSMethodSignature *methodSignature = [[object class] instanceMethodSignatureForSelector:selector]; 获取需要 hook SEL 的方法签名的时候发现,使用的是 instanceMethodSignatureForSelector,所以在需要 hook 类方法的时候,就需要传入 class.metaClass

6、修改类以允许消息拦截 aspect_prepareClassAndHookSelector

这个方法是核心方法,使用 isa_swizzle 来实现的。

image.png

灵魂画手,不要介意哈。

简单介绍一下流程:

有一个类 MyClass 派生出一个对象 myObj,现在需要 hook helloMethod 方法。

  • Aspect 框架通过 aspect_hookClass 方法生成一个 MyClass 的子类 MyClass_Aspect_
  • myObjisa 指向 MyClass_Aspect_
  • 获取 MyClass helloMethod 方法的 IMP
  • MyClass_Aspect_ 中添加方法 aspect_helloMethod 方法,并将 IMP 指向 helloMethod 方法的 IMP(其实就复制了 helloMethod 方法实现)
  • 替换或添加 MyClass_Aspect_ 类中方法列表的 forwardInvcation,让其 IMP 指向 Aspect 框架自己实现的 __ASPECTS_ARE_BEING_CALLED__ 自己处理方法转发
  • 替换或添加 MyClass_Aspect_ 类中方法列表的 helloMethod 方法,让其 IMP 指向 _objc_msgForward

这样调用 MyClass 派生出对象 myObj 的实例方法 helloMethod 的时候其实就是在调用 MyClass_Aspect_ 中的 helloMethod 方法。

但是 helloMethod 方法的 IMP_objc_msgForward(消息转发)就会调用 Aspect 框架自己实现的 IMP __ASPECTS_ARE_BEING_CALLED__

/// 准备类和 hook selector
static void aspect_prepareClassAndHookSelector(NSObject *self, SEL selector, NSError **error) {
    NSCParameterAssert(selector);
    Class klass = aspect_hookClass(self, error);
    
    /// klass  是 原来类的子类。所以能获取到 父类的方法
    Method targetMethod = class_getInstanceMethod(klass, selector);
    
    ///获取方法实现
    IMP targetMethodIMP = method_getImplementation(targetMethod);
    
   
    
    if (!aspect_isMsgForwardIMP(targetMethodIMP)) {
        // Make a method alias for the existing method implementation, it not already copied.
        const char *typeEncoding = method_getTypeEncoding(targetMethod);
        
        /// 给方法增加前缀  AspectsMessagePrefix_
        SEL aliasSelector = aspect_aliasForSelector(selector);
        
        /// 如果新的类 klass 没有实现 aliasSelector
        if (![klass instancesRespondToSelector:aliasSelector]) {
            
            /// klass 类里面加入了 AspectsMessagePrefix_xxx 复制了 xxx 的实现
            __unused BOOL addedAlias = class_addMethod(klass, aliasSelector, method_getImplementation(targetMethod), typeEncoding);
            NSCAssert(addedAlias, @"Original implementation for %@ is already copied to %@ on %@", NSStringFromSelector(selector), NSStringFromSelector(aliasSelector), klass);
        }
        
        /**
         * 到这里 klass (Class_Aspects_)里面方法如下:
         * aspects__viewWillDisappear (在 aspect_prepareClassAndHookSelector .class_addMethod 处添加)
         * class (在 aspect_hookedGetClass 处添加)
         * forwardInvocation (在 aspect_hookClass -> aspect_swizzleForwardInvocation 处添加)
         */


        // We use forwardInvocation to hook in.
        // 给子类添加 原始 selector 方法,让其IMP指向 _objc_msgForward(消息转发)
        class_replaceMethod(klass, selector, aspect_getMsgForwardIMP(self, selector), typeEncoding);
        AspectLog(@"Aspects: Installed hook for -[%@ %@].", klass, NSStringFromSelector(selector));
        
        /**
         * 到这里 klass (Class_Aspects_)里面方法如下:
         * viewWillDisappear: IMP -> _objc_msgForward
         * aspects__viewWillDisappear: imp -> viewWillDisappear
         * class  IMP -> 原始类的cls
         * forwardInvocation: IMP -> __ASPECTS_ARE_BEING_CALLED__
         */
    }
}
6.1 动态生成一个子类 aspect_hookClass
static Class aspect_hookClass(NSObject *self, NSError **error) {
    NSCParameterAssert(self);
    /// object_getClass 是取 isa 也就是self 是对象时,取对象的类,如果是类时,取元类
    /// class self为对象取类,如果self 是类,返回自己。
    /// 如果self 是对象,那么 statedClass == baseClass 
    Class statedClass = self.class;
    Class baseClass = object_getClass(self);
    NSString *className = NSStringFromClass(baseClass);

    // Already subclassed
    if ([className hasSuffix:AspectsSubclassSuffix]) {
            return baseClass;

        // We swizzle a class object, not a single object.
        // 我们混合了一个类对象,而不是单个对象。
    } else if (class_isMetaClass(baseClass)) {
        return aspect_swizzleClassInPlace((Class)self);
        // Probably a KVO'ed class. Swizzle in place. Also swizzle meta classes in place.
    } else if (statedClass != baseClass) {
        return aspect_swizzleClassInPlace(baseClass);
    }

    // Default case. Create dynamic subclass.
    const char *subclassName = [className stringByAppendingString:AspectsSubclassSuffix].UTF8String;
    Class subclass = objc_getClass(subclassName);

    if (subclass == nil) {
         // 动态创建一个类
         subclass = objc_allocateClassPair(baseClass, subclassName, 0);
         if (subclass == nil) {
        // 创建失败了,返回空
        NSString *errrorDesc = [NSString stringWithFormat:@"objc_allocateClassPair failed to allocate class %s.", subclassName];
       AspectError(AspectErrorFailedToAllocateClassPair, errrorDesc);
          return nil;
      }
        // 添加 forwardInvocation 方法
        aspect_swizzleForwardInvocation(subclass);
        
        /**
         * object_getClass是获取isa,其实object_getClass(obj)与[obj class]的区别了,就两点:
         * 1、如果是obj实例对象,他们一样;
         * 2、如果是类对象,class是self,object_getClass是isa
         */
        
        /// 类派生的对象获取类对象,
	aspect_hookedGetClass(subclass, statedClass);
        
        // 防止是元类派生的类对象获取元类对象
	aspect_hookedGetClass(object_getClass(subclass), statedClass);
        
        /// 动态注册类
	objc_registerClassPair(subclass);
    }

    /// 修改isa指针 指向我们的子类 subclass
    object_setClass(self, subclass);
    return subclass;
}
6.2 添加 forwardInvocation 方法 aspect_swizzleForwardInvocation
static NSString *const AspectsForwardInvocationSelectorName = @"__aspects_forwardInvocation:";
static void aspect_swizzleForwardInvocation(Class klass) {
    NSCParameterAssert(klass);
    // If there is no method, replace will act like class_addMethod.
    // 如果没有方法,replace将扮演类似class_addMethod的角色。
    
    // class_replaceMethod: 存在要替换的方法则替换原方法的IMP并返回原方法的IMP,,不存在原有方法则动态添加该方法并且返回nil
    
  
    // 如果实现了 forwardInvocation 会把 forwardInvocation 的IMP 替换成 __ASPECTS_ARE_BEING_CALLED__
    // 如果没有实现,直接添加一个forwardInvocation  IMP 指向__ASPECTS_ARE_BEING_CALLED__
    IMP originalImplementation = class_replaceMethod(klass, @selector(forwardInvocation:), (IMP)__ASPECTS_ARE_BEING_CALLED__, "v@:@");
    
    // 判断是否实现了 forwardInvocation
    if (originalImplementation) {
        // 实现了 forwardInvocation ,需要添加一个 __aspects_forwardInvocation 方法,
        class_addMethod(klass, NSSelectorFromString(AspectsForwardInvocationSelectorName), originalImplementation, "v@:@");
    }
    AspectLog(@"Aspects: %@ is now aspect aware.", NSStringFromClass(klass));
}
6.3 更改 @selector(class) 返回的类 aspect_hookedGetClass

这个方法在iOS框架 KVO 中用过,派生了一个子类用于键值监听,但是调用 class 方法和 objc_getClass() 方法返回是不一样的。

static void aspect_hookedGetClass(Class class, Class statedClass) {
    NSCParameterAssert(class);
    NSCParameterAssert(statedClass);
	Method method = class_getInstanceMethod(class, @selector(class));
	IMP newIMP = imp_implementationWithBlock(^(id self) {
		return statedClass;
	});
    
    /// 添加 class 方法 并且返回之前的的类名
	class_replaceMethod(class, @selector(class), newIMP, method_getTypeEncoding(method));
}

7、Aspects 自己的消息转发 ASPECTS_ARE_BEING_CALLED

这个方法就是拿到对象消息转发了,先处理 Before hooks 调用 block,然后如果有方法替换,处理 Instead hooks. 如果没有方法替换,就循环向父类找调用方法,最后处理 After hooks,处理完销毁 hook。

// This is the swizzled forwardInvocation: method.
static void __ASPECTS_ARE_BEING_CALLED__(__unsafe_unretained NSObject *self, SEL selector, NSInvocation *invocation) {
    NSCParameterAssert(self);
    NSCParameterAssert(invocation);
    SEL originalSelector = invocation.selector;
	SEL aliasSelector = aspect_aliasForSelector(invocation.selector);
    invocation.selector = aliasSelector;
    
    /// 处理对象的 hook
    AspectsContainer *objectContainer = objc_getAssociatedObject(self, aliasSelector);
    
    /// 处理类的 hook
    AspectsContainer *classContainer = aspect_getContainerForClass(object_getClass(self), aliasSelector);
    
    AspectInfo *info = [[AspectInfo alloc] initWithInstance:self invocation:invocation];
    NSArray *aspectsToRemove = nil;

    // Before hooks.
    aspect_invoke(classContainer.beforeAspects, info);
    aspect_invoke(objectContainer.beforeAspects, info);

    // Instead hooks.
    BOOL respondsToAlias = YES;
    if (objectContainer.insteadAspects.count || classContainer.insteadAspects.count) {
        aspect_invoke(classContainer.insteadAspects, info);
        aspect_invoke(objectContainer.insteadAspects, info);
    }else {
        /// 如果没有替换方法就调用原始方法
        Class klass = object_getClass(invocation.target);
        do {
            if ((respondsToAlias = [klass instancesRespondToSelector:aliasSelector])) {
                [invocation invoke];
                break;
            }
        }while (!respondsToAlias && (klass = class_getSuperclass(klass)));
    }

    // After hooks.
    aspect_invoke(classContainer.afterAspects, info);
    aspect_invoke(objectContainer.afterAspects, info);

    // If no hooks are installed, call original implementation (usually to throw an exception)
    if (!respondsToAlias) {
        invocation.selector = originalSelector;
        SEL originalForwardInvocationSEL = NSSelectorFromString(AspectsForwardInvocationSelectorName);
        if ([self respondsToSelector:originalForwardInvocationSEL]) {
            ((void( *)(id, SEL, NSInvocation *))objc_msgSend)(self, originalForwardInvocationSEL, invocation);
        }else {
            [self doesNotRecognizeSelector:invocation.selector];
        }
    }

    // Remove any hooks that are queued for deregistration.
    [aspectsToRemove makeObjectsPerformSelector:@selector(remove)];
}

5、结语

Aspects分析到这里就结束了,本文只对 Aspects 添加 hook 流程进行了分析,没有列出 Aspects 的销毁过程分析,有兴趣可以下载源码看下。

最后 2022 年 元旦,祝大家节日快乐。