Aspects 源码学习
AOP 面向切面编程,在对于埋点、日志记录等操作来说是一个很好的解决方案。而 Aspects 是一个对于AOP编程的一个优雅的实现,也可以直接借助这个库来使用AOP思想。需要值得注意的是,Aspects 是通过消息转发机制的最后一个阶段 ForwardInvocation 来实现的,为了性能,所以这里不要频繁的调用。 github:https://github.com/steipete/Aspects
Aspects的源码学习,我学到的有几下几点
- Objective-C Runtime
- 理解OC的消息分发机制
- KVO中的指针交换技术
- Block 在内存中的数据结构
- const 的修饰区别
- block 中常量在特定情况下的三种处理方法
- 断言语句,
- 自旋锁 使用注意
-
_objc_msgForward_stret 和 _objc_msgForward 前者存在的必要
- 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))); }
有时间在继续研究。先留着