Aspects 源碼學習

AOP 面向切面編程,在對於埋點、日誌記錄等操做來講是一個很好的解決方案。而 Aspects 是一個對於AOP編程的一個優雅的實現,也能夠直接藉助這個庫來使用AOP思想。須要值得注意的是,Aspects   是經過消息轉發機制的最後一個階段 ForwardInvocation 來實現的,爲了性能,因此這裏不要頻繁的調用。  github:https://github.com/steipete/Aspectshtml

Aspects的源碼學習,我學到的有幾下幾點ios

  1. Objective-C Runtime
  2. 理解OC的消息分發機制
  3. KVO中的指針交換技術
  4. Block 在內存中的數據結構
  5. const 的修飾區別
  6. block 中常量在特定狀況下的三種處理方法
  7. 斷言語句,
  8. 自旋鎖 使用注意
  9. _objc_msgForward_stret 和 _objc_msgForward 前者存在的必要git

  10. Type Encoding

   下面是我讀源碼的一些github

 Aspects 的接口只有兩個:objective-c

/// 爲一個指定的類的某個方法執行前/替換/後,添加一段代碼塊.對這個類的全部對象都會起做用.
///
/// @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)));
}

有時間在繼續研究。先留着

相關文章
相關標籤/搜索