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 = , /// Called after the original implementation (default)
- AspectPositionInstead = , /// Will replace the original implementation.
- AspectPositionBefore = , /// Called before the original implementation.
- AspectOptionAutomaticRemoval = << /// 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 = /// (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 = ( << ),
- AspectBlockFlagsHasSignature = ( << )
- };
- 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 += * sizeof(unsigned long int); //desc 地址加上16字节
- if (layout->flags & AspectBlockFlagsHasCopyDisposeHelpers) {//比对layout的第8字节到11字节的第25位 是不是1(1就是有COPY_DISPOSE)
- desc += * 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 > ) {
- const char *blockType = [blockSignature getArgumentTypeAtIndex:];
- if (blockType[] != '@') {
- 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 = ; 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[] != blockType[]) {
- 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[] == _C_STRUCT_B;// 判断方法的类型的编码第一位是不是 _C_STRUCT_B
- if (methodReturnsStructValue) {
- @try {
- NSUInteger valueSize = ;
- NSGetSizeAndAlignment(encoding, &valueSize, NULL);
- if (valueSize == || valueSize == || valueSize == || valueSize == ) { // 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, );
- 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 == && tracker.selectorNamesToSubclassTrackers) {
- [swizzledClassesDict removeObjectForKey:currentClass];
- }
- subclassTracker = tracker;
- }while ((currentClass = class_getSuperclass(currentClass)));
- }
有时间在继续研究。先留着
Aspects 源码学习的更多相关文章
- Spring 源码学习之环境搭建
一.下载Spring 源码 进入 https://github.com/spring-projects/spring-framework/tags 选择下载spring freamework的版本 h ...
- spring源码学习之AOP(一)
继续源码学习,看了spring中基础的容器和AOP感觉自己也没有什么长进,哈哈,我也不知道到底有用没有,这可能是培养自己的一种精神吧,不管那么多,继续学习!AOP中 AOP中几个重要的概念:(1)Ad ...
- Spring5.0源码学习系列之Spring AOP简述
前言介绍 附录:Spring源码学习专栏 在前面章节的学习中,我们对Spring框架的IOC实现源码有了一定的了解,接着本文继续学习Springframework一个核心的技术点AOP技术. 在学习S ...
- Java集合专题总结(1):HashMap 和 HashTable 源码学习和面试总结
2017年的秋招彻底结束了,感觉Java上面的最常见的集合相关的问题就是hash--系列和一些常用并发集合和队列,堆等结合算法一起考察,不完全统计,本人经历:先后百度.唯品会.58同城.新浪微博.趣分 ...
- jQuery源码学习感想
还记得去年(2015)九月份的时候,作为一个大四的学生去参加美团霸面,结果被美团技术总监教育了一番,那次问了我很多jQuery源码的知识点,以前虽然喜欢研究框架,但水平还不足够来研究jQuery源码, ...
- MVC系列——MVC源码学习:打造自己的MVC框架(四:了解神奇的视图引擎)
前言:通过之前的三篇介绍,我们基本上完成了从请求发出到路由匹配.再到控制器的激活,再到Action的执行这些个过程.今天还是趁热打铁,将我们的View也来完善下,也让整个系列相对完整,博主不希望烂尾. ...
- MVC系列——MVC源码学习:打造自己的MVC框架(三:自定义路由规则)
前言:上篇介绍了下自己的MVC框架前两个版本,经过两天的整理,版本三基本已经完成,今天还是发出来供大家参考和学习.虽然微软的Routing功能已经非常强大,完全没有必要再“重复造轮子”了,但博主还是觉 ...
- MVC系列——MVC源码学习:打造自己的MVC框架(二:附源码)
前言:上篇介绍了下 MVC5 的核心原理,整篇文章比较偏理论,所以相对比较枯燥.今天就来根据上篇的理论一步一步进行实践,通过自己写的一个简易MVC框架逐步理解,相信通过这一篇的实践,你会对MVC有一个 ...
- MVC系列——MVC源码学习:打造自己的MVC框架(一:核心原理)
前言:最近一段时间在学习MVC源码,说实话,研读源码真是一个痛苦的过程,好多晦涩的语法搞得人晕晕乎乎.这两天算是理解了一小部分,这里先记录下来,也给需要的园友一个参考,奈何博主技术有限,如有理解不妥之 ...
随机推荐
- 轻松上云,从容实施Office 365项目
这个是我在MVP 社区活动的一节课程,讲述Office 365部署中一些大的挑战和解决的方法 视频URL 例如以下: http://edu.51cto.com/lesson/id-17440.html ...
- 第五章——定时器Timer序言
定时器很重要. 上家公司有用的,是用来做定期数据同步的. 以前老同学有用到,曾经就定时器讨论过一次,还给过一次他我关于spring-task的总结. 但是并没有意识到定时器与多线程的关系,或者说,上一 ...
- spring框架整合springMVC时关于AOP无法切入的问题
最开始springMVC的配置为: spring的配置为: 分析可知道spring的配置正确,由于在springmvc中已经扫描了@Controller相关的注解,所以就不需要再次扫描了,由于spri ...
- EL表达式的简单实用
EL表达式 EL(Expression Language) 是为了使JSP写起来更加简单.表达式语言的灵感来自于 ECMAScript 和 XPath 表达式语言,它提供了在 JSP 中简化表达式的方 ...
- MyBatis简单使用
MyBatis MyBatis 是一款优秀的持久层框架,它支持定制化 SQL.存储过程以及高级映射.MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集.MyBatis 可以使 ...
- [笔记]使用Keepalived实现Nginx主从热备
HA(High Available), 高可用性集群,是保证业务连续性的有效解决方案,一般有两个或两个以上的节点,且分为活动节点及备用节点. 1.1. 高可靠软件keepalived keepaliv ...
- 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 ...
- EditText禁用系统键盘,光标可以继续使用
在项目中有时候需要使用到自己的键盘,那这个时候就不希望系统键盘在弹出,而且光标还要继续显示,其实一个方法就可以简单实现 /** * 禁止Edittext弹出软件盘,光标依然正常显示. */ publi ...
- Akka Serialization
Akka本身使用了Protocol Buffers来序列化内部消息(比如gossip message).Akka系统还可以配置自定义序列化机制. 配置conf akka { actor { ## 在a ...
- Handler的解析和使用
1.handler为android中多线程间通信的一种机制, @1android中只允许在UI线程(主线程)操作或改变UI,其他线程不能操作UI. @2其他线程有刷新UI的需要,所以得告诉UI线程,这 ...