• Aspects 源码学习


    AOP 面向切面编程,在对于埋点、日志记录等操作来说是一个很好的解决方案。而 Aspects 是一个对于AOP编程的一个优雅的实现,也可以直接借助这个库来使用AOP思想。需要值得注意的是,Aspects   是通过消息转发机制的最后一个阶段 ForwardInvocation 来实现的,为了性能,所以这里不要频繁的调用。  github:https://github.com/steipete/Aspects

    Aspects的源码学习,我学到的有几下几点

    1. Objective-C Runtime
    2. 理解OC的消息分发机制
    3. KVO中的指针交换技术
    4. Block 在内存中的数据结构
    5. const 的修饰区别
    6. block 中常量在特定情况下的三种处理方法
    7. 断言语句,
    8. 自旋锁 使用注意
    9. _objc_msgForward_stret 和 _objc_msgForward 前者存在的必要

    10. Type Encoding

       下面是我读源码的一些

     Aspects 的接口只有两个:

    /// 为一个指定的类的某个方法执行前/替换/后,添加一段代码块.对这个类的所有对象都会起作用.
    ///
    /// @param block  方法被添加钩子时,Aspectes会拷贝方法的签名信息.
    /// 第一个参数将会是 `id<AspectInfo>`,余下的参数是此被调用的方法的参数.
    /// 这些参数是可选的,并将被用于传递给block代码块对应位置的参数.
    /// 你甚至使用一个没有任何参数或只有一个`id<AspectInfo>`参数的block代码块.
    ///
    /// @注意 不支持给静态方法添加钩子.
    /// @return 返回一个唯一值,用于取消此钩子.
    + (id<AspectToken>)aspect_hookSelector:(SEL)selector
                          withOptions:(AspectOptions)options
                           usingBlock:(id)block
                                error:(NSError **)error;
    
    /// 为一个指定的对象的某个方法执行前/替换/后,添加一段代码块.只作用于当前对象.
     - (id<AspectToken>)aspect_hookSelector:(SEL)selector withOptions:(AspectOptions)options usingBlock:(id)block error:(NSError **)error; - (id<AspectToken>)aspect_hookSelector:(SEL)selector withOptions:(AspectOptions)options usingBlock:(id)block error:(NSError **)error; 
    /// 撤销一个Aspect 钩子.
    /// @return YES 撤销成功, 否则返回 NO. 
    id<AspectToken> aspect = ...; 
    [aspect remove];

    AspectOptions : 

    typedef NS_OPTIONS(NSUInteger, AspectOptions) {
        AspectPositionAfter   = 0,            /// Called after the original implementation (default)
        AspectPositionInstead = 1,            /// Will replace the original implementation.
        AspectPositionBefore  = 2,            /// Called before the original implementation.
        
        AspectOptionAutomaticRemoval = 1 << 3 /// Will remove the hook after the first execution.
    }; // 定义切片时机

    定义错误信息:

    typedef NS_ENUM(NSUInteger, AspectErrorCode) {
        AspectErrorSelectorBlacklisted,                   /// Selectors like release, retain, autorelease are blacklisted.
        AspectErrorDoesNotRespondToSelector,              /// Selector could not be found.
        AspectErrorSelectorDeallocPosition,               /// When hooking dealloc, only AspectPositionBefore is allowed.
        AspectErrorSelectorAlreadyHookedInClassHierarchy, /// Statically hooking the same method in subclasses is not allowed.
        AspectErrorFailedToAllocateClassPair,             /// The runtime failed creating a class pair.
        AspectErrorMissingBlockSignature,                 /// The block misses compile time signature info and can't be called.
        AspectErrorIncompatibleBlockSignature,            /// The block signature does not match the method or is too large.
    
        AspectErrorRemoveObjectAlreadyDeallocated = 100   /// (for removing) The object hooked is already deallocated.
    };
    /**
     修饰常指针
     const int *A; //const修饰指向的对象,A可变,A指向的对象不可变
     int const *A; //const修饰指向的对象,A可变,A指向的对象不可变
     int *const A; //const修饰指针A, A不可变,A指向的对象可变
     const int *const A;//指针A和A指向的对象都不可变
     
     */
    extern NSString *const AspectErrorDomain;

    在.m 中首先是 定义了一个参与位与运算的枚举,还定义了一个block

    // Block internals.
    typedef NS_OPTIONS(int, AspectBlockFlags) {
        AspectBlockFlagsHasCopyDisposeHelpers = (1 << 25),
        AspectBlockFlagsHasSignature          = (1 << 30)
    };
    typedef struct _AspectBlock { //参考系统的block 定义了一个自己block,关于block学习 https://maniacdev.com/2013/11/tutorial-an-in-depth-guide-to-objective-c-block-debugging
        __unused Class isa; // __unused  如果变量没有使用就不会参与编译,也不会报warning
        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;
     

    然后是几个类定义

    AspectInfo,主要是 NSInvocation 信息。将NSInvocation包装一层,比如参数信息等。便于直接使用。

    AspectIdentifier,一个Aspect的具体内容。主要包含了单个的 aspect 的具体信息,包括执行时机,要执行 block 所需要用到的具体信息:包括方法签名、参数等等。其实就是将我们传入的bloc,包装成AspectIdentifier,便于后续使用。通过我们替换的block实例化。也就是将我们传入的block,包装成了AspectIdentifier

    AspectsContainer,一个对象或者类的所有的 Aspects 整体情况,注意这里数组是通过atomic修饰的。

    AspectTracker,用于跟踪所改变的类,打上标记,用于替换类方法,防止重复替换类方法。

    接下来定义了一个参与(切片时机)位与运算的宏 

    #define AspectPositionFilter 0x07 // 二进制就是111

    将具体的`AspectIdentifier `添加到容器中 

    static id aspect_add(id self, SEL selector, AspectOptions options, id block, NSError **error) {
        /**
         NSCParameterAssert 针对C函数的参数断言 详情参考https://www.jianshu.com/p/6e444981ab45
         NSAssert 针对OC方法的条件断言
         NSCAssert  针对C函数的条件断言  NSCAssert(a == 2, @"a must equal to 2"); //第一个参数是条件,如果第一个参数不满足条件,就会记录并打印后面的字符串
         NSParameterAssert  针对OC方法的参数断言 NSParameterAssert(str); //只需要一个参数,如果参数存在程序继续运行,如果参数为空,则程序停止打印日志
         NSCparameterAssert 针对C函数的参数断言
         */
        NSCParameterAssert(self);
        NSCParameterAssert(selector);
        NSCParameterAssert(block);
    
        __block AspectIdentifier *identifier = nil; //关于__block学习了 禅与Objective-C编程 https://github.com/oa414/objc-zen-book-cn/
        aspect_performLocked(^{
            if (aspect_isSelectorAllowedAndTrack(self, selector, options, error)) {
                AspectsContainer *aspectContainer = aspect_getContainerForObject(self, selector);
                identifier = [AspectIdentifier identifierWithSelector:selector object:self options:options block:block error:error];
                if (identifier) {
                    [aspectContainer addAspect:identifier withOptions:options];
    
                    // Modify the class to allow message interception.
                    aspect_prepareClassAndHookSelector(self, selector, error);
                }
            }
        });
        return identifier;
    }

    //自旋锁,如果访问这个锁的线程不是同一优先级的话,可能会造成死锁。具体原因请看 不再安全的 OSSpinLock
    static void aspect_performLocked(dispatch_block_t block) {
        static OSSpinLock aspect_lock = OS_SPINLOCK_INIT;
        OSSpinLockLock(&aspect_lock);
        //加锁执行block
        block();
        //释放锁
        OSSpinLockUnlock(&aspect_lock);
    }

    合成selector的别名 加上@“aspects__”

    static SEL aspect_aliasForSelector(SEL selector) {
    
        NSCParameterAssert(selector);
    
       
    
    return NSSelectorFromString([AspectsMessagePrefix stringByAppendingFormat:@"_%@", NSStringFromSelector(selector)]);
    
    }

    具体的block内存结构定义、flags和block 中定义的枚举掩码 参考http://clang.llvm.org/docs/Block-ABI-Apple.html

    将block 签名转换为方法签名

    static NSMethodSignature *aspect_blockMethodSignature(id block, NSError **error) {
        //// 将block转换为自定义的block形式
        AspectBlockRef layout = (__bridge void *)block;
        if (!(layout->flags & AspectBlockFlagsHasSignature)) {// 比对layout的第8字节到11字节的第三十位 是不是1(1就是有签名)
            NSString *description = [NSString stringWithFormat:@"The block %@ doesn't contain a type signature.", block];
            AspectError(AspectErrorMissingBlockSignature, description);
            return nil;
        }
        void *desc = layout->descriptor;
        desc += 2 * sizeof(unsigned long int); //desc 地址加上16字节
        if (layout->flags & AspectBlockFlagsHasCopyDisposeHelpers) {//比对layout的第8字节到11字节的第25位 是不是1(1就是有COPY_DISPOSE)
            desc += 2 * sizeof(void *); //desc 再加 8 字节,这时候的地址才是真正signature的地址
        }
        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 的签名

    static BOOL aspect_isCompatibleBlockSignature(NSMethodSignature *blockSignature, id object, SEL selector, NSError **error) {
        NSCParameterAssert(blockSignature);
        NSCParameterAssert(object);
        NSCParameterAssert(selector);
    
        BOOL signaturesMatch = YES;
        NSMethodSignature *methodSignature = [[object class] instanceMethodSignatureForSelector:selector];
        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.
            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;
    }

    手动调用消息转发

    static BOOL aspect_isMsgForwardIMP(IMP impl) {
        return impl == _objc_msgForward
    #if !defined(__arm64__)
        || impl == (IMP)_objc_msgForward_stret
    #endif
        ;
    }
    static IMP aspect_getMsgForwardIMP(NSObject *self, SEL selector) {
        IMP msgForwardIMP = _objc_msgForward;
    #if !defined(__arm64__)  // 在 arm64 的架构上  是va_arg 的结构体 改变了, 所以针对非arm64 的结构进行了处理
        // As an ugly internal runtime implementation detail in the 32bit runtime, we need to determine of the method we hook returns a struct or anything larger than id.
        // https://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/LowLevelABI/000-Introduction/introduction.html
        // https://github.com/ReactiveCocoa/ReactiveCocoa/issues/783
        // http://infocenter.arm.com/help/topic/com.arm.doc.ihi0042e/IHI0042E_aapcs.pdf (Section 5.4)
        Method method = class_getInstanceMethod(self.class, selector);
        const char *encoding = method_getTypeEncoding(method);
        BOOL methodReturnsStructValue = encoding[0] == _C_STRUCT_B;// 判断方法的类型的编码第一位是不是 _C_STRUCT_B
        if (methodReturnsStructValue) {
            @try {
                NSUInteger valueSize = 0;
                NSGetSizeAndAlignment(encoding, &valueSize, NULL);
    
                if (valueSize == 1 || valueSize == 2 || valueSize == 4 || valueSize == 8) { // i386 架构  http://sealiesoftware.com/blog/archive/2008/10/30/objc_explain_objc_msgSend_stret.html
                    methodReturnsStructValue = NO;
                }
            } @catch (__unused NSException *e) {}
        }
        if (methodReturnsStructValue) {
            msgForwardIMP = (IMP)_objc_msgForward_stret;
        }
    #endif
        return msgForwardIMP;
    }

    以下是核心类的实现,原作者的注释已经很清晰了

    static void aspect_prepareClassAndHookSelector(NSObject *self, SEL selector, NSError **error) {
        NSCParameterAssert(selector);
        Class klass = aspect_hookClass(self, error);
        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);
            SEL aliasSelector = aspect_aliasForSelector(selector);
            if (![klass instancesRespondToSelector:aliasSelector]) {
                __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.
            class_replaceMethod(klass, selector, aspect_getMsgForwardIMP(self, selector), typeEncoding);
            AspectLog(@"Aspects: Installed hook for -[%@ %@].", klass, NSStringFromSelector(selector));
        }
    }
    
    // Will undo the runtime changes made.
    static void aspect_cleanupHookedClassAndSelector(NSObject *self, SEL selector) {
        NSCParameterAssert(self);
        NSCParameterAssert(selector);
    
        Class klass = object_getClass(self);
        BOOL isMetaClass = class_isMetaClass(klass);
        if (isMetaClass) {
            klass = (Class)self;
        }
    
        // Check if the method is marked as forwarded and undo that.
        Method targetMethod = class_getInstanceMethod(klass, selector);
        IMP targetMethodIMP = method_getImplementation(targetMethod);
        if (aspect_isMsgForwardIMP(targetMethodIMP)) {
            // Restore the original method implementation.
            const char *typeEncoding = method_getTypeEncoding(targetMethod);
            SEL aliasSelector = aspect_aliasForSelector(selector);
            Method originalMethod = class_getInstanceMethod(klass, aliasSelector);
            IMP originalIMP = method_getImplementation(originalMethod);
            NSCAssert(originalMethod, @"Original implementation for %@ not found %@ on %@", NSStringFromSelector(selector), NSStringFromSelector(aliasSelector), klass);
    
            class_replaceMethod(klass, selector, originalIMP, typeEncoding);
            AspectLog(@"Aspects: Removed hook for -[%@ %@].", klass, NSStringFromSelector(selector));
        }
    
        // Deregister global tracked selector
        aspect_deregisterTrackedSelector(self, selector);
    
        // Get the aspect container and check if there are any hooks remaining. Clean up if there are not.
        AspectsContainer *container = aspect_getContainerForObject(self, selector);
        if (!container.hasAspects) {
            // Destroy the container
            aspect_destroyContainerForObject(self, selector);
    
            // Figure out how the class was modified to undo the changes.
            NSString *className = NSStringFromClass(klass);
            if ([className hasSuffix:AspectsSubclassSuffix]) {
                Class originalClass = NSClassFromString([className stringByReplacingOccurrencesOfString:AspectsSubclassSuffix withString:@""]);
                NSCAssert(originalClass != nil, @"Original class must exist");
                object_setClass(self, originalClass);
                AspectLog(@"Aspects: %@ has been restored.", NSStringFromClass(originalClass));
    
                // We can only dispose the class pair if we can ensure that no instances exist using our subclass.
                // Since we don't globally track this, we can't ensure this - but there's also not much overhead in keeping it around.
                //objc_disposeClassPair(object.class);
            }else {
                // Class is most likely swizzled in place. Undo that.
                if (isMetaClass) {
                    aspect_undoSwizzleClassInPlace((Class)self);
                }else if (self.class != klass) {
                    aspect_undoSwizzleClassInPlace(klass);
                }
            }
        }
    }

    HOOK Class  主要是 swizzling 类/对象的 forwardInvocation 函数

    aspects 的真正的处理逻辑都是在 forwradInvocation 函数里面进行的。对于对象实例而言,源代码中并没有直接 swizzling 对象的 forwardInvocation 方法,而是动态生成一个当前对象的子类,并将当前对象与子类关联,然后替换子类的 forwardInvocation 方法(这里具体方法就是调用了 object_setClass(self, subclass) ,将当前对象 isa 指针指向了 subclass ,同时修改了 subclass 以及其 subclass metaclass 的 class 方法,使他返回当前对象的 class。,这个地方特别绕,它的原理有点类似 kvo 的实现,它想要实现的效果就是,将当前对象变成一个 subclass 的实例,同时对于外部使用者而言,又能把它继续当成原对象在使用,而且所有的 swizzling 操作都发生在子类,这样做的好处是你不需要去更改对象本身的类,也就是,当你在 remove aspects 的时候,如果发现当前对象的 aspect 都被移除了,那么,你可以将 isa 指针重新指回对象本身的类,从而消除了该对象的 swizzling ,同时也不会影响到其他该类的不同对象)。对于每一个对象而言,这样的动态对象只会生成一次,这里 aspect_swizzlingForwardInvocation 将使得 forwardInvocation 方法指向 aspects 自己的实现逻辑。

     当前对象的子类-----isa指向----> 当前对象----isa指向---->当前类

    当前对象----isa指向---->当前对象的子类----isa指向---->当前类

    当remove aspects后,当前对象的isa指向当前类

    static Class aspect_hookClass(NSObject *self, NSError **error) {
        NSCParameterAssert(self);
        /**
         [self class] 返回self的类对象
         object_getClass(self) 返回isa指向的类对象,
         */
        Class statedClass = self.class;
        Class baseClass = object_getClass(self); // isa指向的类,那如果是被KVO的属性 怎么办?
        NSString *className = NSStringFromClass(baseClass);
    
        // Already subclassed   判断是否已经是添加过_Aspects_而创建的子类了,是就直接返回
        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. 创建 _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;
            }
           
            aspect_swizzleForwardInvocation(subclass);
            aspect_hookedGetClass(subclass, statedClass);
            aspect_hookedGetClass(object_getClass(subclass), statedClass);
            objc_registerClassPair(subclass);
        }
    
        object_setClass(self, subclass);//将当前self设置为子类,这里其实只是更改了self的isa指针而已
    return subclass;
    }

        

    ForwardInvocation: IMP指向__ASPECTS_ARE_BEING_CALLED__  ,再创建 __aspects_forwardInvocation:指向原来的IMP

    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.
        IMP originalImplementation = class_replaceMethod(klass, @selector(forwardInvocation:), (IMP)__ASPECTS_ARE_BEING_CALLED__, "v@:@");//type Encoding https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html#//apple_ref/doc/uid/TP40008048-CH100-SW1
        if (originalImplementation) {
            class_addMethod(klass, NSSelectorFromString(AspectsForwardInvocationSelectorName), originalImplementation, "v@:@");
        }
        AspectLog(@"Aspects: %@ is now aspect aware.", NSStringFromClass(klass));
    }

     替换子类class 方法的IMP,

    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_replaceMethod(class, @selector(class), newIMP, method_getTypeEncoding(method));
    }

    经过Swizzle forwardInvocation 的类都保存在这里

    static Class aspect_swizzleClassInPlace(Class klass) {
        NSCParameterAssert(klass);
        NSString *className = NSStringFromClass(klass);
    
        _aspect_modifySwizzledClasses(^(NSMutableSet *swizzledClasses) {
            if (![swizzledClasses containsObject:className]) {
                aspect_swizzleForwardInvocation(klass);
                [swizzledClasses addObject:className];
            }
        });
        return klass;
    }

    对参数进行检查是否合格

    static BOOL aspect_isSelectorAllowedAndTrack(NSObject *self, SEL selector, AspectOptions options, NSError **error) {
        static NSSet *disallowedSelectorList;
        static dispatch_once_t pred;
        dispatch_once(&pred, ^{
            disallowedSelectorList = [NSSet setWithObjects:@"retain", @"release", @"autorelease", @"forwardInvocation:", nil];
        });
    
        // Check against the blacklist.  检查传进来的selector是不是黑名单中的 @"retain", @"release", @"autorelease", @"forwardInvocation:"
        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;// 0x000 0x010 0x100 前三个 和0x111进行&运算,作者想要的结果是 AspectPositionBefore 0x010
        if ([selectorName isEqualToString:@"dealloc"] && position != AspectPositionBefore) {
            NSString *errorDesc = @"AspectPositionBefore is the only valid position when hooking dealloc.";
            AspectError(AspectErrorSelectorDeallocPosition, errorDesc);
            return NO;
        }
        //类或者对象 必须响应这个 selector
        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))) {
            Class klass = [self class];
            NSMutableDictionary *swizzledClassesDict = aspect_getSwizzledClassesDict();
            Class currentClass = [self class];
            //swizzledClassesDict  仅仅是初始化了可变字典,没有存值,怎么就取值了呢
            AspectTracker *tracker = swizzledClassesDict[currentClass];
            if ([tracker subclassHasHookedSelectorName:selectorName]) {
                
                NSSet *subclassTracker = [tracker subclassTrackersHookingSelectorName:selectorName];
                NSSet *subclassNames = [subclassTracker valueForKey:@"trackedClassName"];
                NSString *errorDescription = [NSString stringWithFormat:@"Error: %@ already hooked subclasses: %@. A method can only be hooked once per class hierarchy.", selectorName, subclassNames];
                AspectError(AspectErrorSelectorAlreadyHookedInClassHierarchy, errorDescription);
                return NO;
            }
    
     
            do {
                tracker = swizzledClassesDict[currentClass];
                if ([tracker.selectorNames containsObject:selectorName]) {
                    if (klass == currentClass) {
                        // Already modified and topmost!  一个类只能hook 一次
                        return YES;
                    }
                    NSString *errorDescription = [NSString stringWithFormat:@"Error: %@ already hooked in %@. A method can only be hooked once per class hierarchy.", selectorName, NSStringFromClass(currentClass)];
                    AspectError(AspectErrorSelectorAlreadyHookedInClassHierarchy, errorDescription);
                    return NO;
                }
            } while ((currentClass = class_getSuperclass(currentClass)));//只有当这个类为根类的时候才不会继续循环查找。
    
            // Add the selector as being modified.
            currentClass = klass;
            AspectTracker *subclassTracker = nil;
            do {
                tracker = swizzledClassesDict[currentClass];
                if (!tracker) {
                    tracker = [[AspectTracker alloc] initWithTrackedClass:currentClass];
                    swizzledClassesDict[(id<NSCopying>)currentClass] = tracker;
                }
                if (subclassTracker) {
                    [tracker addSubclassTracker:subclassTracker hookingSelectorName:selectorName];
                } else {
                    [tracker.selectorNames addObject:selectorName];
                }
    
                // All superclasses get marked as having a subclass that is modified.
                subclassTracker = tracker;
            }while ((currentClass = class_getSuperclass(currentClass)));
        } else {
            return YES;
        }
    
        return YES;
    }

    取消已经跟踪的selector

    static void aspect_deregisterTrackedSelector(id self, SEL selector) {
        if (!class_isMetaClass(object_getClass(self))) return;
    
        NSMutableDictionary *swizzledClassesDict = aspect_getSwizzledClassesDict();
        NSString *selectorName = NSStringFromSelector(selector);
        Class currentClass = [self class];
        AspectTracker *subclassTracker = nil;
        do {
            AspectTracker *tracker = swizzledClassesDict[currentClass];
            if (subclassTracker) {
                [tracker removeSubclassTracker:subclassTracker hookingSelectorName:selectorName];
            } else {
                [tracker.selectorNames removeObject:selectorName];
            }
            if (tracker.selectorNames.count == 0 && tracker.selectorNamesToSubclassTrackers) {
                [swizzledClassesDict removeObjectForKey:currentClass];
            }
            subclassTracker = tracker;
        }while ((currentClass = class_getSuperclass(currentClass)));
    }

    有时间在继续研究。先留着

  • 相关阅读:
    Nginx如何配置Http、Https、WS、WSS?
    关于MySQL日志,我与阿里P9都聊了些啥?
    一文搞懂MySQL体系架构!!
    在业务高峰期拔掉服务器电源是一种怎样的体验?
    千万不要轻易尝试“熊猫烧香”,这不,我后悔了!
    从小白程序员到大厂高级技术专家我看过哪些书籍?
    聊一聊我在 B 站自学 Java 的经历吧
    计算机网络的 89 个核心概念
    MQTT 协议是个啥?这篇文章告诉你!
    1.5w字 + 24张图肝翻 TCP。
  • 原文地址:https://www.cnblogs.com/DafaRan/p/8192069.html
Copyright © 2020-2023  润新知