美文网首页
Aspects解析

Aspects解析

作者: freemanIT | 来源:发表于2022-04-29 16:15 被阅读0次

    Aspects

    AOP(Aspect-oriented programming) 面向切面编程

    通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术, 统一注入代码片段而不需修改原有代码逻辑, 和继承方式对比, 代码耦合度更低

    iOS 中实现AOP 的框架Aspects 是其中之一, 提供实例和类方法对类中方法进行hook, 在原方法运行前/中/后, 插入代码片段. 原理为把所有的方法调用指向_objc_msgForward, 处理原方法的参数列表和返回值, 最后修改forwardInvocation 方法使用NSInvocation 动态调用

    应用到技术

    • 消息转发(Message Forward)
    • 运行时(Runtime)

    添加

    添加add

    移除

    移除remove

    结构

    结构

    两个协议

    • AspectToken 用于注销Hook
    • AspectInfo Hook中的Block 的首位参数

    4 个类

    • AspectInfo 切面信息, 遵循AspectInfo 协议
    • AspectsContainer ID, 遵循AspectToken 协议
    • AspectsContainer 容器
    • AspectTracker 跟踪器

    结构体

    • AspectBlockRef 内部定义的Block 结构体, _AspectBlock_

    源码分析

    交换流程

    例如交换viewWillAppear

    1. 将hook 的selector 指向objc_msgForward / objc_msgForward_stret
    2. 生成aliasSelector 指向原selector 的IMp
    3. 将forwardInvocation 指向自定义的_ASPECTS_ARE_BEING_CALLED_
    4. 生成_aspects_forwardInvocation 指向原forwardInvocation 的IMP

    调用比较简单, 两个方法

    + (id<AspectToken>)aspect_hookSelector:(SEL)selector
                          withOptions:(AspectOptions)options
                           usingBlock:(id)block
                                error:(NSError **)error {
        return aspect_add((id)self, selector, options, block, error);
    }
    
    /// @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 {
        return aspect_add(self, selector, options, block, error);
    }
    

    遵循了AspectToken 协议

    @protocol AspectToken <NSObject>
    
    // 注销一个 aspect.
    - (BOOL)remove;
    
    @end
    

    内部都调用了aspect_add 函数

    // 这里的self 可以是实例对象, 也可以是类对象
    static id aspect_add(id self, SEL selector, AspectOptions options, id block, NSError **error) {
        NSCParameterAssert(self);
        NSCParameterAssert(selector);
        NSCParameterAssert(block);
    
        __block AspectIdentifier *identifier = nil;
        aspect_performLocked(^{
            // 是否允许进行hook 操作, 判断白名单
            if (aspect_isSelectorAllowedAndTrack(self, selector, options, error)) {
                // 通过objc_getAssociatedObject 获取混写方法容器
                AspectsContainer *aspectContainer = aspect_getContainerForObject(self, selector);
                // 将block 封装到AspectIdentifier 对象中, 并将这个对象保存到aspectContainer 对象中
                identifier = [AspectIdentifier identifierWithSelector:selector object:self options:options block:block error:error];
                if (identifier) {
                    // 根据标识将方法放在对应的容器中
                    [aspectContainer addAspect:identifier withOptions:options];
                    // block 封装好了开始下一步, method swizzling 和转发消息
                    // Modify the class to allow message interception.
                    aspect_prepareClassAndHookSelector(self, selector, error);
                }
            }
        });
        return identifier;
    }
    

    在调用aspect_prepareClassAndHookSelector 之前做了判断

    1. 检查方法能否被hook, retain, release, forwardInvocation 方法禁止被hook
    2. 获取类中的AspectsContainer 容器, 封装成AspectIdentifier , 其中对参数兼容进行判断
    3. AspectIdentifier 放入到对应容器中

    aspect_prepareClassAndHookSelector 关键代码实现

    /** 为了实现消息转发, 进行method swizzling 操作
     1. 首先进行swizzleForwardInvocation 的实现替换为自定义方法 __ASPECTS_ARE_BEING_CALLED__
     并添加selector __aspects_forwardInvocation: 设置其实现为forwardInvocation 原来的实现, 需要进行消息转发的selector 都会执行__ASPECTS_ARE_BEING_CALLED__
     2. 将hook 的selector 的实现替换为_objc_msgForward 或者_objc_msgForward_stret, 具体是根据selector 的返回值类型确定, 同事添加selector :aspect_aliasForSelector, 其实现为selector 原来的实现, 此时调用selector  时就会进行消息转发
     */
    static void aspect_prepareClassAndHookSelector(NSObject *self, SEL selector, NSError **error) {
        NSCParameterAssert(selector);
        // 改写forwardInvocation 的实现
        // 传入的self 得到其指向的类
        // 如果传入是类对象则Hook 其forwardInvocation 方法, 将Container 内的方法混写进去, 再将class/metaClass 返回
        // 如果是实例对象, 则通过动态创建子类的方式返回新创建的子类
        Class klass = aspect_hookClass(self, error);
    
        Method targetMethod = class_getInstanceMethod(klass, selector);
        IMP targetMethodIMP = method_getImplementation(targetMethod);
        // swizzle 要hook 的selector, 如果selector 的实现是_objc_msgForward 或者_objc_msgForward_stret, 就不进行swizzle 了
        if (!aspect_isMsgForwardIMP(targetMethodIMP)) {
            // Make a method alias for the existing method implementation, it not already copied.
            const char *typeEncoding = method_getTypeEncoding(targetMethod);
            // 添加别名方法aspects_xxx, 并将别名方法的实现指向原方法的实现
            SEL aliasSelector = aspect_aliasForSelector(selector);
            if (![klass instancesRespondToSelector:aliasSelector]) { // 没有使用aliasSelector 保存selector 原来的实现
                __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);
            }
    
            // We use forwardInvocation to hook in.
            // 将被hook 方法的实现改为消息转发
            // 原方法替换为_objc_msgForward 或者_objc_msgForward_stret 形式触发, 进入消息转发机制forwardInvocation
            // 其中判断arm64
            class_replaceMethod(klass, selector, aspect_getMsgForwardIMP(self, selector), typeEncoding);
            AspectLog(@"Aspects: Installed hook for -[%@ %@].", klass, NSStringFromSelector(selector));
        }
    }
    

    通过aspect_hookClass 获取目标类, 替换forwardInvocation 方法, 原方法实现替换为_objc_msgForward 或是 _objc_msgForward_stret, 进入到消息转发, 调用forwardInvocation

    aspect_hookClass 获取目标类

    static Class aspect_hookClass(NSObject *self, NSError **error) {
        NSCParameterAssert(self);
        // 1. self 为实例对象时, 下面两个返回的结果是一样的, 结果是isa 指针所指向的对象, 指向类对象
        // 2. self 为class 类对象时, object_getClass 返回isa 指向的元类对象, 为MetaClass 时指向根元类, 为RootClass 时, 指向根元类, 为RootMetaClass 时指向自身
        Class statedClass = self.class;
        Class baseClass = object_getClass(self);
        NSString *className = NSStringFromClass(baseClass);
        // 当hook 一个对象的selector 时会产生一个子类, 子类后缀是AspectsSubclassSuffix, 当self 对应的类就是生成的子类, 直接返回
        // Already subclassed
        if ([className hasSuffix:AspectsSubclassSuffix]) {
            return baseClass;
    
            // We swizzle a class object, not a single object.
        }else if (class_isMetaClass(baseClass)) {
            // 若self 是类对象或是元类对象, 则混写self, 替换forwardInvocation 方法
            return aspect_swizzleClassInPlace((Class)self);
            // Probably a KVO'ed class. Swizzle in place. Also swizzle meta classes in place.
        }else if (statedClass != baseClass) {
            // statedClass != baseClass, 并且不满足上述两个条件, 则说明是KVO 模式下的实例对象, 混写metaClass
            return aspect_swizzleClassInPlace(baseClass);
        }
    
        // Default case. Create dynamic subclass.
        // 上述条件都不满足, 说明是实例对象
        // 动态为hook 的实例的类添加子类, 替换实例对象isa 指针, 指向新创建子类来实现Aspects
        // 当hook 一个对象的selector 时, 实现原理和KVO 类似, 1. 生成一个子类 2. aspect_swizzleForwardInvocation
        // 拼接_Aspects_ 后缀成新类名
        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;
            }
            // 改写subclass 的forwardInvocation 方法, 插入Aspects
            aspect_swizzleForwardInvocation(subclass);
            // 改写subclass 的.class 方法, 使其返回self.class
            aspect_hookedGetClass(subclass, statedClass);
            // subclass 的isa 的.class 方法, 返回self.class
            aspect_hookedGetClass(object_getClass(subclass), statedClass);
            // 注册子类
            objc_registerClassPair(subclass);
        }
        // 将关联的实例的类型设定为新的添加的子类
        // 更改isa 指针
        object_setClass(self, subclass);
        return subclass;
    }
    

    aspect_hookClass 对实例和类对象分别做了不同处理. self.class 和objc_getClass(self) 的值判断当前对象环境

    1. 子类化的实例对象, 直接返回它的类
    2. 类对象和元类对象, aspect_swizzleClassInPlace, 替换forwardInvocation
    3. KVO 模式的实例对象, aspect_swizzleClassInPlace, 替换forwardInvocation
    4. 实例对象, 创建_Aspects_ 结尾的子类, 再替换forwardInvocation 的实现和实例对象isa 指针

    _ASPECTS_ARE_BEING_CALLED_ 方法调用, 消息转发都会来到这里

    // 消息经过转发后都会来这里, 包括手动和自动消息转发, 在这里统一处理, 调用block, 调用原来方法实现
    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;
        AspectsContainer *objectContainer = objc_getAssociatedObject(self, aliasSelector);
        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 {
            // 没有instead hooks 时就执行selector 被hook 之前的实现
            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)
        // 调用一个没有实现selector 会触发自动消息转发, 在这种情况下, 整个继承链中都不会响应aliasSelector 也就导致了respondsToAlias == false, 开始执行下面的方法
        if (!respondsToAlias) {
            invocation.selector = originalSelector;
            SEL originalForwardInvocationSEL = NSSelectorFromString(AspectsForwardInvocationSelectorName);
            // 如果实现了forwardInvocation 执行原来的消息转发, 否则调用doesNotRecognizeSelector, 抛出异常
            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)];
    }
    

    AspectInfo 遵循了AspectInfo 协议, 作为block 的第一个参数

    /// AspectInfo 协议是我们块语法的第一个参数。
    @protocol AspectInfo <NSObject>
    
    /// 当前被 Hook 的实例
    - (id)instance;
    
    /// 被 Hook 方法的原始 invocation
    - (NSInvocation *)originalInvocation;
    
    /// 所有方法参数(装箱之后的)惰性执行
    - (NSArray *)arguments;
    
    @end
    

    AspectInfo 类Aspect 的环境, hook 的实例, 调用方法和参数

    // Aspect 的环境, 包含被hook 的实例, 调用方法和参数
    // 这个是Block 的第一个参数
    // 遵守AspectInfo 协议
    @interface AspectInfo : NSObject <AspectInfo>
    - (id)initWithInstance:(__unsafe_unretained id)instance invocation:(NSInvocation *)invocation;
    @property (nonatomic, unsafe_unretained, readonly) id instance;
    @property (nonatomic, strong, readonly) NSArray *arguments;
    @property (nonatomic, strong, readonly) NSInvocation *originalInvocation;
    @end
        
    @implementation AspectInfo
    
    @synthesize arguments = _arguments;
    
    - (id)initWithInstance:(__unsafe_unretained id)instance invocation:(NSInvocation *)invocation {
        NSCParameterAssert(instance);
        NSCParameterAssert(invocation);
        if (self = [super init]) {
            _instance = instance;
            _originalInvocation = invocation;
        }
        return self;
    }
    
    // 懒加载arguments, 获取NSInvocation 中保存的参数值
    - (NSArray *)arguments {
        // Lazily evaluate arguments, boxing is expensive.
        if (!_arguments) {
            _arguments = self.originalInvocation.aspects_arguments;
        }
        return _arguments;
    }
    
    @end
    

    AspectIdentifier 包含一次完整的Aspect 过程

    // 每进行一个hook, 都会生成一个AspectIdentifier 对应
    // 包含一次完整Aspect 的所有内容
    @interface AspectIdentifier : NSObject
    + (instancetype)identifierWithSelector:(SEL)selector object:(id)object options:(AspectOptions)options block:(id)block error:(NSError **)error;
    - (BOOL)invokeWithInfo:(id<AspectInfo>)info;
    @property (nonatomic, assign) SEL selector;
    @property (nonatomic, strong) id block;
    @property (nonatomic, strong) NSMethodSignature *blockSignature;
    @property (nonatomic, weak) id object;
    @property (nonatomic, assign) AspectOptions options;
    @end
        
    // 没进行一次hook 操作, 就会生成一个对应的AspectIdentifier 对象, 相当于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.
        // 判断要Hook 的方法是否兼容block 的参数个数和参数类型
        if (!aspect_isCompatibleBlockSignature(blockSignature, object, selector, error)) {
            return nil;
        }
        // 组装AspectIdentifier
        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;
    }
    

    AspectsContainer 用于盛放AspectIdentifier 对象

    // 可以理解为容器, 用于盛放AspectIdentifier 对象, 一个selector 对应一个AspectsContainer 对象, 使用objc_setAssociatedObject 存储在实例对象或者类对象上, key 就是selector
    @interface AspectsContainer : NSObject
    - (void)addAspect:(AspectIdentifier *)aspect withOptions:(AspectOptions)injectPosition;
    - (BOOL)removeAspect:(id)aspect;
    - (BOOL)hasAspects;
    @property (atomic, copy) NSArray *beforeAspects;
    @property (atomic, copy) NSArray *insteadAspects;
    @property (atomic, copy) NSArray *afterAspects;
    @end
    

    AspectTracker 跟踪器

    /** 每一个class 对应一个AspectTracker, 在一个继承链上一个selector 只能被hook 一次, AspectTracker 就是实现这种需求的数据结构, Aspect hook 一个selector 是通过method swizzlling 配合消息转发实现的, 如果一个继承链中对同一个selector 进行多次swizzlling, 会出现意想不到的效果*/
    @interface AspectTracker : NSObject
    - (id)initWithTrackedClass:(Class)trackedClass;
    @property (nonatomic, strong) Class trackedClass;
    @property (nonatomic, readonly) NSString *trackedClassName;
    // trackedClass hook 的selector 集合
    @property (nonatomic, strong) NSMutableSet *selectorNames;
    // 记录子类的hook selector 的信息[HookingSelectorName: (AspectTracker1, AspectTracker2, ...)]
    @property (nonatomic, strong) NSMutableDictionary *selectorNamesToSubclassTrackers;
    - (void)addSubclassTracker:(AspectTracker *)subclassTracker hookingSelectorName:(NSString *)selectorName;
    - (void)removeSubclassTracker:(AspectTracker *)subclassTracker hookingSelectorName:(NSString *)selectorName;
    - (BOOL)subclassHasHookedSelectorName:(NSString *)selectorName;
    - (NSSet *)subclassTrackersHookingSelectorName:(NSString *)selectorName;
    @end
        
    // 判断子类是否hook 了selector
    - (BOOL)subclassHasHookedSelectorName:(NSString *)selectorName;
    
    // 当子类hook selector 后, 将与子类对应的AspectTracker 对象保存到superClass 中, 这种数据结构的设计就是为了实现在一个类继承链中一个selector 只能被hook 一次
    - (void)addSubclassTracker:(AspectTracker *)subclassTracker hookingSelectorName:(NSString *)selectorName;
    
    // 在父类中删除某个子类的selectorName 对应的AspectTracker 对象
    - (void)removeSubclassTracker:(AspectTracker *)subclassTracker hookingSelectorName:(NSString *)selectorName;
    
    // 如果该类hook 某个selector, 此selector 已经被子类hook 过, 为了保证一个类继承链中一个selector 只能被hook 一次, 此时显示错误信息, 提示subClass1, 已经hook 过此selector
    - (NSSet *)subclassTrackersHookingSelectorName:(NSString *)selectorName;
    

    AspectBlockRef 结构体

    // Aspects 封装block
    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 internals.
    /*
     从block源码(libclosure)可知
     struct Block_layout {
     void *isa;
     int flags;
     int reserved;
     void (*invoke)(void *, ...);
     struct Block_descriptor *descriptor;
    };
    struct Block_descriptor {
        unsigned long int reserved;
        unsigned long int size;
        void (*copy)(void *dst, void *src);
        void (*dispose)(void *);
    };
     // Values for Block_layout->flags to describe block objects
     enum {
     BLOCK_DEALLOCATING =      (0x0001),  // runtime
     BLOCK_REFCOUNT_MASK =     (0xfffe),  // runtime
     BLOCK_NEEDS_FREE =        (1 << 24), // runtime
     BLOCK_HAS_COPY_DISPOSE =  (1 << 25), // compiler
     BLOCK_HAS_CTOR =          (1 << 26), // compiler: helpers have C++ code
     BLOCK_IS_GC =             (1 << 27), // runtime
     BLOCK_IS_GLOBAL =         (1 << 28), // compiler
     BLOCK_USE_STRET =         (1 << 29), // compiler: undefined if !BLOCK_HAS_SIGNATURE
     BLOCK_HAS_SIGNATURE  =    (1 << 30), // compiler
     BLOCK_HAS_EXTENDED_LAYOUT=(1 << 31)  // compiler
     };
     */
    
    // AspectIdentifier 初始化中的代码
    // 获取block 签名, 强制转换block 为AspectBlockRef, 根据标志位和结构体的结构, 获取signature
    static NSMethodSignature *aspect_blockMethodSignature(id block, NSError **error) {
        // 强制转换
        AspectBlockRef layout = (__bridge void *)block;
        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;
        // 指针位置是size 后一位
        desc += 2 * sizeof(unsigned long int);
        // 如果捕获外界变量, 是堆的block 则有void (*copy), void (*dispose), 指针再偏移2 * sizeof(void *)
        if (layout->flags & AspectBlockFlagsHasCopyDisposeHelpers) {
            desc += 2 * sizeof(void *);
        }
        if (!desc) {
            NSString *description = [NSString stringWithFormat:@"The block %@ doesn't has a type signature.", block];
            AspectError(AspectErrorMissingBlockSignature, description);
            return nil;
        }
        // 此时指针位置对应*signature
        const char *signature = (*(const char **)desc);
        return [NSMethodSignature signatureWithObjCTypes:signature];
    }
    
    // block 的参数列表吆喝被hook 的selector 的参数列表匹配, 从index = 2 开始验证, 从下面的代码中可以看出验证规则
    static BOOL aspect_isCompatibleBlockSignature(NSMethodSignature *blockSignature, id object, SEL selector, NSError **error) {
        NSCParameterAssert(blockSignature);
        NSCParameterAssert(object);
        NSCParameterAssert(selector);
    
        BOOL signaturesMatch = YES;
        // hook 一个类方法, instanceMethodSignatureForSelector: 一定会返回nil
        // 返回方法签名
        NSMethodSignature *methodSignature = [[object class] instanceMethodSignatureForSelector:selector];
        // blockSignature.numberOfArguments = 2 > 0, 返回NO, 在上层方法中结束hook 流程
        // 两个签名参数数量, block 的参数不能超过Hook 方法的参数数量
        // 在一般的方法签名中block 类型签名是没有self('@') 或者_cmd(':'), 只有一个参数代表自己('@?')
        // 所以Method 签名的前两个参数编码固定为'@' 和':'
        // block 签名的第一个参数固定为'@?'
        if (blockSignature.numberOfArguments > methodSignature.numberOfArguments) {
            signaturesMatch = NO;
        }else {
            if (blockSignature.numberOfArguments > 1) {
                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.
            // argument[0] 是self/block, argument[1] 是SEL/id<AspectInfo>, 所以从argument[2] 开始比较
            // block 的参数数量可以少于Hook 方法的参数数量, 但是每个位置类型必须相同
            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.
                    if (!methodType || !blockType || methodType[0] != blockType[0]) {
                        signaturesMatch = NO; break;
                    }
                }
            }
        }
        // 抛异常
        if (!signaturesMatch) {
            NSString *description = [NSString stringWithFormat:@"Block signature %@ doesn't match %@.", blockSignature, methodSignature];
            AspectError(AspectErrorIncompatibleBlockSignature, description);
            return NO;
        }
        return YES;
    }
    

    缺点, 调用函数到objc_msgForward 要经过多个消息转发, 消耗额外的内存取构建Invocation, 不适合处理频繁调用的方法, 而函数最后都转移到objc_msgForward, 导致其他Hook 方式不兼容, 对同一个函数先后使用Aspects 和class_replaceMethod, 导致class_replaceMethod 获取的原方法为objc_msgForward, 异常甚至崩溃

    关于_objc_msgForward_stret

    大多数CPU在执行C函数时会把前几个参数放进寄存器里,对 obj_msgSend 来说前两个参数固定是 self / _cmd,它们会放在寄存器上,在最后执行完后返回值也会保存在寄存器上,取这个寄存器的值就是返回值。普通的返回值(int/pointer)很小,放在寄存器上没问题,但有些 struct 是很大的,寄存器放不下,所以要用另一种方式,在一开始申请一段内存,把指针保存在寄存器上,返回值往这个指针指向的内存写数据,所以寄存器要腾出一个位置放这个指针,self / _cmd 在寄存器的位置就变了。objc_msgSend 不知道 self / _cmd 的位置变了,所以要用另一个方法 objc_msgSend_stret 代替。原理大概就是这样。在 NSMethodSignature 的 debugDescription 上打出了是否 special struct,只能通过这字符串判断。所以最终的处理是,在非 arm64 下,是 special struct 就走 _objc_msgForward_stret,否则走 _objc_msgForward。

    关于signatureWithObjCTypes

    在OC 中, 每一种数据类型可以通过一个字符编码表示, 例如'@' 代表一个object, 'i' 代表int, 由这些字符组成的数组就可以代表方法类型

    • 'v' void 类型
    • ':' 对应SEL

    参考:

    1. iOS之class方法和object_getClass方法的区别
    2. 深入理解Aspects源码
    3. Aspects源码分析
    4. 神经病院objc runtime入院考试
    5. 神经病院 Objective-C Runtime 入院第一天—— isa 和 Class
    6. 深入理解Objective-C:Category
    7. Baymax:网易iOS App运行时Crash自动防护实践
    8. iOS 界的毒瘤:Method Swizzle
    9. 面向切面编程之 Aspects 源码解析及应用
    10. 刨根问底objective-c runtime
    11. RunTime应用实例--关于埋点的思考
    12. 你真的会判断 _objc_msgForward_stret 吗

    相关文章

      网友评论

          本文标题:Aspects解析

          本文链接:https://www.haomeiwen.com/subject/uzddyrtx.html