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 的接口只有两个:

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

AspectOptions :

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

定义错误信息:

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

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

  1. // Block internals.
  2. typedef NS_OPTIONS(int, AspectBlockFlags) {
  3. AspectBlockFlagsHasCopyDisposeHelpers = ( << ),
  4. AspectBlockFlagsHasSignature = ( << )
  5. };
  6. typedef struct _AspectBlock { //参考系统的block 定义了一个自己block,关于block学习 https://maniacdev.com/2013/11/tutorial-an-in-depth-guide-to-objective-c-block-debugging
  7. __unused Class isa; // __unused 如果变量没有使用就不会参与编译,也不会报warning
  8. AspectBlockFlags flags;
  9. __unused int reserved;
  10. void (__unused *invoke)(struct _AspectBlock *block, ...);
  11. struct {
  12. unsigned long int reserved;
  13. unsigned long int size;
  14. // requires AspectBlockFlagsHasCopyDisposeHelpers
  15. void (*copy)(void *dst, const void *src);
  16. void (*dispose)(const void *);
  17. // requires AspectBlockFlagsHasSignature
  18. const char *signature;
  19. const char *layout;
  20. } *descriptor;
  21. // imported variables
  22. } *AspectBlockRef;
  1.  

然后是几个类定义

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

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

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

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

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

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

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

  1. static id aspect_add(id self, SEL selector, AspectOptions options, id block, NSError **error) {
  2. /**
  3. NSCParameterAssert 针对C函数的参数断言 详情参考https://www.jianshu.com/p/6e444981ab45
  4. NSAssert 针对OC方法的条件断言
  5. NSCAssert 针对C函数的条件断言 NSCAssert(a == 2, @"a must equal to 2"); //第一个参数是条件,如果第一个参数不满足条件,就会记录并打印后面的字符串
  6. NSParameterAssert 针对OC方法的参数断言 NSParameterAssert(str); //只需要一个参数,如果参数存在程序继续运行,如果参数为空,则程序停止打印日志
  7. NSCparameterAssert 针对C函数的参数断言
  8. */
  9. NSCParameterAssert(self);
  10. NSCParameterAssert(selector);
  11. NSCParameterAssert(block);
  12.  
  13. __block AspectIdentifier *identifier = nil; //关于__block学习了 禅与Objective-C编程 https://github.com/oa414/objc-zen-book-cn/
  14. aspect_performLocked(^{
  15. if (aspect_isSelectorAllowedAndTrack(self, selector, options, error)) {
  16. AspectsContainer *aspectContainer = aspect_getContainerForObject(self, selector);
  17. identifier = [AspectIdentifier identifierWithSelector:selector object:self options:options block:block error:error];
  18. if (identifier) {
  19. [aspectContainer addAspect:identifier withOptions:options];
  20.  
  21. // Modify the class to allow message interception.
  22. aspect_prepareClassAndHookSelector(self, selector, error);
  23. }
  24. }
  25. });
  26. return identifier;
  27. }
  1. //自旋锁,如果访问这个锁的线程不是同一优先级的话,可能会造成死锁。具体原因请看 不再安全的 OSSpinLock
  1. static void aspect_performLocked(dispatch_block_t block) {
  2. static OSSpinLock aspect_lock = OS_SPINLOCK_INIT;
  3. OSSpinLockLock(&aspect_lock);
  4. //加锁执行block
  5. block();
  6. //释放锁
  7. OSSpinLockUnlock(&aspect_lock);
  8. }

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

  1. static SEL aspect_aliasForSelector(SEL selector) {
  2.  
  3. NSCParameterAssert(selector);
  4.  
  5. return NSSelectorFromString([AspectsMessagePrefix stringByAppendingFormat:@"_%@", NSStringFromSelector(selector)]);
  6.  
  7. }

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

将block 签名转换为方法签名

  1. static NSMethodSignature *aspect_blockMethodSignature(id block, NSError **error) {
  2. //// 将block转换为自定义的block形式
  3. AspectBlockRef layout = (__bridge void *)block;
  4. if (!(layout->flags & AspectBlockFlagsHasSignature)) {// 比对layout的第8字节到11字节的第三十位 是不是1(1就是有签名)
  5. NSString *description = [NSString stringWithFormat:@"The block %@ doesn't contain a type signature.", block];
  6. AspectError(AspectErrorMissingBlockSignature, description);
  7. return nil;
  8. }
  9. void *desc = layout->descriptor;
  10. desc += * sizeof(unsigned long int); //desc 地址加上16字节
  11. if (layout->flags & AspectBlockFlagsHasCopyDisposeHelpers) {//比对layout的第8字节到11字节的第25位 是不是1(1就是有COPY_DISPOSE)
  12. desc += * sizeof(void *); //desc 再加 8 字节,这时候的地址才是真正signature的地址
  13. }
  14. if (!desc) {
  15. NSString *description = [NSString stringWithFormat:@"The block %@ doesn't has a type signature.", block];
  16. AspectError(AspectErrorMissingBlockSignature, description);
  17. return nil;
  18. }
  19. // 转化成NSMethodSignature 对象输出签名
  20. const char *signature = (*(const char **)desc);
  21. //根据类型编码返回真正方法签名
  22. return [NSMethodSignature signatureWithObjCTypes:signature];
  23. }

比较方法和block 的签名

  1. static BOOL aspect_isCompatibleBlockSignature(NSMethodSignature *blockSignature, id object, SEL selector, NSError **error) {
  2. NSCParameterAssert(blockSignature);
  3. NSCParameterAssert(object);
  4. NSCParameterAssert(selector);
  5.  
  6. BOOL signaturesMatch = YES;
  7. NSMethodSignature *methodSignature = [[object class] instanceMethodSignatureForSelector:selector];
  8. if (blockSignature.numberOfArguments > methodSignature.numberOfArguments) {
  9. signaturesMatch = NO;
  10. }else {
  11. if (blockSignature.numberOfArguments > ) {
  12. const char *blockType = [blockSignature getArgumentTypeAtIndex:];
  13. if (blockType[] != '@') {
  14. signaturesMatch = NO;
  15. }
  16. }
  17. // Argument 0 is self/block, argument 1 is SEL or id<AspectInfo>. We start comparing at argument 2.
  18. // The block can have less arguments than the method, that's ok.
  19. if (signaturesMatch) {
  20. for (NSUInteger idx = ; idx < blockSignature.numberOfArguments; idx++) {
  21. const char *methodType = [methodSignature getArgumentTypeAtIndex:idx];
  22. const char *blockType = [blockSignature getArgumentTypeAtIndex:idx];
  23. // Only compare parameter, not the optional type data. 只比较参数的类型
  24. if (!methodType || !blockType || methodType[] != blockType[]) {
  25. signaturesMatch = NO; break;
  26. }
  27. }
  28. }
  29. }
  30.  
  31. if (!signaturesMatch) {
  32. NSString *description = [NSString stringWithFormat:@"Block signature %@ doesn't match %@.", blockSignature, methodSignature];
  33. AspectError(AspectErrorIncompatibleBlockSignature, description);
  34. return NO;
  35. }
  36. return YES;
  37. }

手动调用消息转发

  1. static BOOL aspect_isMsgForwardIMP(IMP impl) {
  2. return impl == _objc_msgForward
  3. #if !defined(__arm64__)
  4. || impl == (IMP)_objc_msgForward_stret
  5. #endif
  6. ;
  7. }
  1. static IMP aspect_getMsgForwardIMP(NSObject *self, SEL selector) {
  2. IMP msgForwardIMP = _objc_msgForward;
  3. #if !defined(__arm64__) // 在 arm64 的架构上 是va_arg 的结构体 改变了, 所以针对非arm64 的结构进行了处理
  4. // 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.
  5. // https://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/LowLevelABI/000-Introduction/introduction.html
  6. // https://github.com/ReactiveCocoa/ReactiveCocoa/issues/783
  7. // http://infocenter.arm.com/help/topic/com.arm.doc.ihi0042e/IHI0042E_aapcs.pdf (Section 5.4)
  8. Method method = class_getInstanceMethod(self.class, selector);
  9. const char *encoding = method_getTypeEncoding(method);
  10. BOOL methodReturnsStructValue = encoding[] == _C_STRUCT_B;// 判断方法的类型的编码第一位是不是 _C_STRUCT_B
  11. if (methodReturnsStructValue) {
  12. @try {
  13. NSUInteger valueSize = ;
  14. NSGetSizeAndAlignment(encoding, &valueSize, NULL);
  15.  
  16. if (valueSize == || valueSize == || valueSize == || valueSize == ) { // i386 架构 http://sealiesoftware.com/blog/archive/2008/10/30/objc_explain_objc_msgSend_stret.html
  17. methodReturnsStructValue = NO;
  18. }
  19. } @catch (__unused NSException *e) {}
  20. }
  21. if (methodReturnsStructValue) {
  22. msgForwardIMP = (IMP)_objc_msgForward_stret;
  23. }
  24. #endif
  25. return msgForwardIMP;
  26. }

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

  1. static void aspect_prepareClassAndHookSelector(NSObject *self, SEL selector, NSError **error) {
  2. NSCParameterAssert(selector);
  3. Class klass = aspect_hookClass(self, error);
  4. Method targetMethod = class_getInstanceMethod(klass, selector);
  5. IMP targetMethodIMP = method_getImplementation(targetMethod);
  6.  
  7. if (!aspect_isMsgForwardIMP(targetMethodIMP)) {
  8. // Make a method alias for the existing method implementation, it not already copied.
  9. const char *typeEncoding = method_getTypeEncoding(targetMethod);
  10. SEL aliasSelector = aspect_aliasForSelector(selector);
  11. if (![klass instancesRespondToSelector:aliasSelector]) {
  12. __unused BOOL addedAlias = class_addMethod(klass, aliasSelector, method_getImplementation(targetMethod), typeEncoding);
  13. NSCAssert(addedAlias, @"Original implementation for %@ is already copied to %@ on %@", NSStringFromSelector(selector), NSStringFromSelector(aliasSelector), klass);
  14. }
  15.  
  16. // We use forwardInvocation to hook in.
  17. class_replaceMethod(klass, selector, aspect_getMsgForwardIMP(self, selector), typeEncoding);
  18. AspectLog(@"Aspects: Installed hook for -[%@ %@].", klass, NSStringFromSelector(selector));
  19. }
  20. }
  21.  
  22. // Will undo the runtime changes made.
  23. static void aspect_cleanupHookedClassAndSelector(NSObject *self, SEL selector) {
  24. NSCParameterAssert(self);
  25. NSCParameterAssert(selector);
  26.  
  27. Class klass = object_getClass(self);
  28. BOOL isMetaClass = class_isMetaClass(klass);
  29. if (isMetaClass) {
  30. klass = (Class)self;
  31. }
  32.  
  33. // Check if the method is marked as forwarded and undo that.
  34. Method targetMethod = class_getInstanceMethod(klass, selector);
  35. IMP targetMethodIMP = method_getImplementation(targetMethod);
  36. if (aspect_isMsgForwardIMP(targetMethodIMP)) {
  37. // Restore the original method implementation.
  38. const char *typeEncoding = method_getTypeEncoding(targetMethod);
  39. SEL aliasSelector = aspect_aliasForSelector(selector);
  40. Method originalMethod = class_getInstanceMethod(klass, aliasSelector);
  41. IMP originalIMP = method_getImplementation(originalMethod);
  42. NSCAssert(originalMethod, @"Original implementation for %@ not found %@ on %@", NSStringFromSelector(selector), NSStringFromSelector(aliasSelector), klass);
  43.  
  44. class_replaceMethod(klass, selector, originalIMP, typeEncoding);
  45. AspectLog(@"Aspects: Removed hook for -[%@ %@].", klass, NSStringFromSelector(selector));
  46. }
  47.  
  48. // Deregister global tracked selector
  49. aspect_deregisterTrackedSelector(self, selector);
  50.  
  51. // Get the aspect container and check if there are any hooks remaining. Clean up if there are not.
  52. AspectsContainer *container = aspect_getContainerForObject(self, selector);
  53. if (!container.hasAspects) {
  54. // Destroy the container
  55. aspect_destroyContainerForObject(self, selector);
  56.  
  57. // Figure out how the class was modified to undo the changes.
  58. NSString *className = NSStringFromClass(klass);
  59. if ([className hasSuffix:AspectsSubclassSuffix]) {
  60. Class originalClass = NSClassFromString([className stringByReplacingOccurrencesOfString:AspectsSubclassSuffix withString:@""]);
  61. NSCAssert(originalClass != nil, @"Original class must exist");
  62. object_setClass(self, originalClass);
  63. AspectLog(@"Aspects: %@ has been restored.", NSStringFromClass(originalClass));
  64.  
  65. // We can only dispose the class pair if we can ensure that no instances exist using our subclass.
  66. // Since we don't globally track this, we can't ensure this - but there's also not much overhead in keeping it around.
  67. //objc_disposeClassPair(object.class);
  68. }else {
  69. // Class is most likely swizzled in place. Undo that.
  70. if (isMetaClass) {
  71. aspect_undoSwizzleClassInPlace((Class)self);
  72. }else if (self.class != klass) {
  73. aspect_undoSwizzleClassInPlace(klass);
  74. }
  75. }
  76. }
  77. }

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指向当前类

  1. static Class aspect_hookClass(NSObject *self, NSError **error) {
  2. NSCParameterAssert(self);
  3. /**
  4. [self class] 返回self的类对象
  5. object_getClass(self) 返回isa指向的类对象,
  6. */
  7. Class statedClass = self.class;
  8. Class baseClass = object_getClass(self); // isa指向的类,那如果是被KVO的属性 怎么办?
  9. NSString *className = NSStringFromClass(baseClass);
  10.  
  11. // Already subclassed 判断是否已经是添加过_Aspects_而创建的子类了,是就直接返回
  12. if ([className hasSuffix:AspectsSubclassSuffix]) {
  13. return baseClass;
  14.  
  15. // We swizzle a class object, not a single object.
  16. }else if (class_isMetaClass(baseClass)) {
  17. return aspect_swizzleClassInPlace((Class)self);
  18. // Probably a KVO'ed class. Swizzle in place. Also swizzle meta classes in place.//原来是这样
  19. }else if (statedClass != baseClass) {
  20. return aspect_swizzleClassInPlace(baseClass);
  21. }
  22.  
  23. // Default case. Create dynamic subclass. 创建 _Aspects_ 后缀动态子类
  24. const char *subclassName = [className stringByAppendingString:AspectsSubclassSuffix].UTF8String;
  25. Class subclass = objc_getClass(subclassName);
  26.  
  27. if (subclass == nil) {
  28. subclass = objc_allocateClassPair(baseClass, subclassName, );
  29. if (subclass == nil) {
  30. NSString *errrorDesc = [NSString stringWithFormat:@"objc_allocateClassPair failed to allocate class %s.", subclassName];
  31. AspectError(AspectErrorFailedToAllocateClassPair, errrorDesc);
  32. return nil;
  33. }
  34.  
  35. aspect_swizzleForwardInvocation(subclass);
  36. aspect_hookedGetClass(subclass, statedClass);
  37. aspect_hookedGetClass(object_getClass(subclass), statedClass);
  38. objc_registerClassPair(subclass);
  39. }
  40.  
  41. object_setClass(self, subclass);//将当前self设置为子类,这里其实只是更改了self的isa指针而已
  1. return subclass;
  2. }

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

  1. static NSString *const AspectsForwardInvocationSelectorName = @"__aspects_forwardInvocation:";
  2. static void aspect_swizzleForwardInvocation(Class klass) {
  3. NSCParameterAssert(klass);
  4. // If there is no method, replace will act like class_addMethod.
  5. 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
  6. if (originalImplementation) {
  7. class_addMethod(klass, NSSelectorFromString(AspectsForwardInvocationSelectorName), originalImplementation, "v@:@");
  8. }
  9. AspectLog(@"Aspects: %@ is now aspect aware.", NSStringFromClass(klass));
  10. }

替换子类class 方法的IMP,

  1. static void aspect_hookedGetClass(Class class, Class statedClass) {
  2. NSCParameterAssert(class);
  3. NSCParameterAssert(statedClass);
  4. Method method = class_getInstanceMethod(class, @selector(class));
  5. IMP newIMP = imp_implementationWithBlock(^(id self) {
  6. return statedClass;
  7. });
  8.  
  9. class_replaceMethod(class, @selector(class), newIMP, method_getTypeEncoding(method));
  10. }

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

  1. static Class aspect_swizzleClassInPlace(Class klass) {
  2. NSCParameterAssert(klass);
  3. NSString *className = NSStringFromClass(klass);
  4.  
  5. _aspect_modifySwizzledClasses(^(NSMutableSet *swizzledClasses) {
  6. if (![swizzledClasses containsObject:className]) {
  7. aspect_swizzleForwardInvocation(klass);
  8. [swizzledClasses addObject:className];
  9. }
  10. });
  11. return klass;
  12. }

对参数进行检查是否合格

  1. static BOOL aspect_isSelectorAllowedAndTrack(NSObject *self, SEL selector, AspectOptions options, NSError **error) {
  2. static NSSet *disallowedSelectorList;
  3. static dispatch_once_t pred;
  4. dispatch_once(&pred, ^{
  5. disallowedSelectorList = [NSSet setWithObjects:@"retain", @"release", @"autorelease", @"forwardInvocation:", nil];
  6. });
  7.  
  8. // Check against the blacklist. 检查传进来的selector是不是黑名单中的 @"retain", @"release", @"autorelease", @"forwardInvocation:"
  9. NSString *selectorName = NSStringFromSelector(selector);
  10. if ([disallowedSelectorList containsObject:selectorName]) {
  11. NSString *errorDescription = [NSString stringWithFormat:@"Selector %@ is blacklisted.", selectorName];
  12. AspectError(AspectErrorSelectorBlacklisted, errorDescription);
  13. return NO;
  14. }
  15.  
  16. // Additional checks. dealloc 只能在调用前hook
  17. AspectOptions position = options&AspectPositionFilter;// 0x000 0x010 0x100 前三个 和0x111进行&运算,作者想要的结果是 AspectPositionBefore 0x010
  18. if ([selectorName isEqualToString:@"dealloc"] && position != AspectPositionBefore) {
  19. NSString *errorDesc = @"AspectPositionBefore is the only valid position when hooking dealloc.";
  20. AspectError(AspectErrorSelectorDeallocPosition, errorDesc);
  21. return NO;
  22. }
  23. //类或者对象 必须响应这个 selector
  24. if (![self respondsToSelector:selector] && ![self.class instancesRespondToSelector:selector]) {
  25. NSString *errorDesc = [NSString stringWithFormat:@"Unable to find selector -[%@ %@].", NSStringFromClass(self.class), selectorName];
  26. AspectError(AspectErrorDoesNotRespondToSelector, errorDesc);
  27. return NO;
  28. }
  29.  
  30. // Search for the current class and the class hierarchy IF we are modifying a class object
  31. if (class_isMetaClass(object_getClass(self))) {
  32. Class klass = [self class];
  33. NSMutableDictionary *swizzledClassesDict = aspect_getSwizzledClassesDict();
  34. Class currentClass = [self class];
  35. //swizzledClassesDict 仅仅是初始化了可变字典,没有存值,怎么就取值了呢
  36. AspectTracker *tracker = swizzledClassesDict[currentClass];
  37. if ([tracker subclassHasHookedSelectorName:selectorName]) {
  38.  
  39. NSSet *subclassTracker = [tracker subclassTrackersHookingSelectorName:selectorName];
  40. NSSet *subclassNames = [subclassTracker valueForKey:@"trackedClassName"];
  41. NSString *errorDescription = [NSString stringWithFormat:@"Error: %@ already hooked subclasses: %@. A method can only be hooked once per class hierarchy.", selectorName, subclassNames];
  42. AspectError(AspectErrorSelectorAlreadyHookedInClassHierarchy, errorDescription);
  43. return NO;
  44. }
  45.  
  46. do {
  47. tracker = swizzledClassesDict[currentClass];
  48. if ([tracker.selectorNames containsObject:selectorName]) {
  49. if (klass == currentClass) {
  50. // Already modified and topmost! 一个类只能hook 一次
  51. return YES;
  52. }
  53. NSString *errorDescription = [NSString stringWithFormat:@"Error: %@ already hooked in %@. A method can only be hooked once per class hierarchy.", selectorName, NSStringFromClass(currentClass)];
  54. AspectError(AspectErrorSelectorAlreadyHookedInClassHierarchy, errorDescription);
  55. return NO;
  56. }
  57. } while ((currentClass = class_getSuperclass(currentClass)));//只有当这个类为根类的时候才不会继续循环查找。
  58.  
  59. // Add the selector as being modified.
  60. currentClass = klass;
  61. AspectTracker *subclassTracker = nil;
  62. do {
  63. tracker = swizzledClassesDict[currentClass];
  64. if (!tracker) {
  65. tracker = [[AspectTracker alloc] initWithTrackedClass:currentClass];
  66. swizzledClassesDict[(id<NSCopying>)currentClass] = tracker;
  67. }
  68. if (subclassTracker) {
  69. [tracker addSubclassTracker:subclassTracker hookingSelectorName:selectorName];
  70. } else {
  71. [tracker.selectorNames addObject:selectorName];
  72. }
  73.  
  74. // All superclasses get marked as having a subclass that is modified.
  75. subclassTracker = tracker;
  76. }while ((currentClass = class_getSuperclass(currentClass)));
  77. } else {
  78. return YES;
  79. }
  80.  
  81. return YES;
  82. }

取消已经跟踪的selector

  1. static void aspect_deregisterTrackedSelector(id self, SEL selector) {
  2. if (!class_isMetaClass(object_getClass(self))) return;
  3.  
  4. NSMutableDictionary *swizzledClassesDict = aspect_getSwizzledClassesDict();
  5. NSString *selectorName = NSStringFromSelector(selector);
  6. Class currentClass = [self class];
  7. AspectTracker *subclassTracker = nil;
  8. do {
  9. AspectTracker *tracker = swizzledClassesDict[currentClass];
  10. if (subclassTracker) {
  11. [tracker removeSubclassTracker:subclassTracker hookingSelectorName:selectorName];
  12. } else {
  13. [tracker.selectorNames removeObject:selectorName];
  14. }
  15. if (tracker.selectorNames.count == && tracker.selectorNamesToSubclassTrackers) {
  16. [swizzledClassesDict removeObjectForKey:currentClass];
  17. }
  18. subclassTracker = tracker;
  19. }while ((currentClass = class_getSuperclass(currentClass)));
  20. }

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

Aspects 源码学习的更多相关文章

  1. Spring 源码学习之环境搭建

    一.下载Spring 源码 进入 https://github.com/spring-projects/spring-framework/tags 选择下载spring freamework的版本 h ...

  2. spring源码学习之AOP(一)

    继续源码学习,看了spring中基础的容器和AOP感觉自己也没有什么长进,哈哈,我也不知道到底有用没有,这可能是培养自己的一种精神吧,不管那么多,继续学习!AOP中 AOP中几个重要的概念:(1)Ad ...

  3. Spring5.0源码学习系列之Spring AOP简述

    前言介绍 附录:Spring源码学习专栏 在前面章节的学习中,我们对Spring框架的IOC实现源码有了一定的了解,接着本文继续学习Springframework一个核心的技术点AOP技术. 在学习S ...

  4. Java集合专题总结(1):HashMap 和 HashTable 源码学习和面试总结

    2017年的秋招彻底结束了,感觉Java上面的最常见的集合相关的问题就是hash--系列和一些常用并发集合和队列,堆等结合算法一起考察,不完全统计,本人经历:先后百度.唯品会.58同城.新浪微博.趣分 ...

  5. jQuery源码学习感想

    还记得去年(2015)九月份的时候,作为一个大四的学生去参加美团霸面,结果被美团技术总监教育了一番,那次问了我很多jQuery源码的知识点,以前虽然喜欢研究框架,但水平还不足够来研究jQuery源码, ...

  6. MVC系列——MVC源码学习:打造自己的MVC框架(四:了解神奇的视图引擎)

    前言:通过之前的三篇介绍,我们基本上完成了从请求发出到路由匹配.再到控制器的激活,再到Action的执行这些个过程.今天还是趁热打铁,将我们的View也来完善下,也让整个系列相对完整,博主不希望烂尾. ...

  7. MVC系列——MVC源码学习:打造自己的MVC框架(三:自定义路由规则)

    前言:上篇介绍了下自己的MVC框架前两个版本,经过两天的整理,版本三基本已经完成,今天还是发出来供大家参考和学习.虽然微软的Routing功能已经非常强大,完全没有必要再“重复造轮子”了,但博主还是觉 ...

  8. MVC系列——MVC源码学习:打造自己的MVC框架(二:附源码)

    前言:上篇介绍了下 MVC5 的核心原理,整篇文章比较偏理论,所以相对比较枯燥.今天就来根据上篇的理论一步一步进行实践,通过自己写的一个简易MVC框架逐步理解,相信通过这一篇的实践,你会对MVC有一个 ...

  9. MVC系列——MVC源码学习:打造自己的MVC框架(一:核心原理)

    前言:最近一段时间在学习MVC源码,说实话,研读源码真是一个痛苦的过程,好多晦涩的语法搞得人晕晕乎乎.这两天算是理解了一小部分,这里先记录下来,也给需要的园友一个参考,奈何博主技术有限,如有理解不妥之 ...

随机推荐

  1. 轻松上云,从容实施Office 365项目

    这个是我在MVP 社区活动的一节课程,讲述Office 365部署中一些大的挑战和解决的方法 视频URL 例如以下: http://edu.51cto.com/lesson/id-17440.html ...

  2. 第五章——定时器Timer序言

    定时器很重要. 上家公司有用的,是用来做定期数据同步的. 以前老同学有用到,曾经就定时器讨论过一次,还给过一次他我关于spring-task的总结. 但是并没有意识到定时器与多线程的关系,或者说,上一 ...

  3. spring框架整合springMVC时关于AOP无法切入的问题

    最开始springMVC的配置为: spring的配置为: 分析可知道spring的配置正确,由于在springmvc中已经扫描了@Controller相关的注解,所以就不需要再次扫描了,由于spri ...

  4. EL表达式的简单实用

    EL表达式 EL(Expression Language) 是为了使JSP写起来更加简单.表达式语言的灵感来自于 ECMAScript 和 XPath 表达式语言,它提供了在 JSP 中简化表达式的方 ...

  5. MyBatis简单使用

    MyBatis MyBatis 是一款优秀的持久层框架,它支持定制化 SQL.存储过程以及高级映射.MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集.MyBatis 可以使 ...

  6. [笔记]使用Keepalived实现Nginx主从热备

    HA(High Available), 高可用性集群,是保证业务连续性的有效解决方案,一般有两个或两个以上的节点,且分为活动节点及备用节点. 1.1. 高可靠软件keepalived keepaliv ...

  7. ERROR: Java 1.7 or later is required to run Apache Drill.

    问题 Apache 的 drill 执行启动命令 drill-embedded 报错: ERROR: Java 1.7 or later is required to run Apache Drill ...

  8. EditText禁用系统键盘,光标可以继续使用

    在项目中有时候需要使用到自己的键盘,那这个时候就不希望系统键盘在弹出,而且光标还要继续显示,其实一个方法就可以简单实现 /** * 禁止Edittext弹出软件盘,光标依然正常显示. */ publi ...

  9. Akka Serialization

    Akka本身使用了Protocol Buffers来序列化内部消息(比如gossip message).Akka系统还可以配置自定义序列化机制. 配置conf akka { actor { ## 在a ...

  10. Handler的解析和使用

    1.handler为android中多线程间通信的一种机制, @1android中只允许在UI线程(主线程)操作或改变UI,其他线程不能操作UI. @2其他线程有刷新UI的需要,所以得告诉UI线程,这 ...