AOP 面向切面編程,在對於埋點、日誌記錄等操做來講是一個很好的解決方案。而 Aspects 是一個對於AOP編程的一個優雅的實現,也能夠直接藉助這個庫來使用AOP思想。須要值得注意的是,Aspects 是經過消息轉發機制的最後一個階段 ForwardInvocation 來實現的,爲了性能,因此這裏不要頻繁的調用。 github:https://github.com/steipete/Aspectshtml
Aspects的源碼學習,我學到的有幾下幾點ios
_objc_msgForward_stret 和 _objc_msgForward 前者存在的必要git
下面是我讀源碼的一些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];
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))); }
有時間在繼續研究。先留着