Aspects
分析- (id<AspectToken>)aspect_hookSelector:(SEL)selector withOptions:(AspectOptions)options usingBlock:(id)block error:(NSError **)error
aspect_add:主要步骤
自旋锁执行下面hook操作,
1.取出AspectsContainer 对像,通过关联对象存储的值。
2.通过传入的selector,obj,block,等参数初始化AspectIdentifier,将AspectIdentifier 添加到AspectsContainer数组中去。
3.执行aspect_prepareClassAndHookSelector(self, selector, error);这个方法主要去给对象添加一个子类或者给类hook forwarding方法。后面详细分析aspect_prepareClassAndHookSelector的具体分析,
aspect_add代码如下:
中间类代码:
里面涉及到的中间类分析
1.AspectIdentifier 这个是每个hook 保存的aspect ,包含selector,block,blockSignature,object,options,其中blockSignature这个很重要,用到结构体去对应block,通过指针偏移查找到blockSignature,结构体代码如下:
2.AspectsContainer 这个类是用来保存所有的AspectIdentifier//Tracks all aspects for an object/class,,有三种数组beforeAspects,insteadAspects,afterAspects,(copy,atomic)属性。
3.AspectTracker 这个是打上标志的类,如果当前类已经hook了一个类方法,这个类及其父类都会保存selectorName,下次hook直接判断,不能再hook。
aspect_prepareClassAndHookSelector的具体分析代码如下:
1.aspect_hookClass的主要作用判断是否有后缀AspectsSubclassSuffix,说明之前hook过这个对象,中间类已经生成,判断是否元类,或者kvo之类的有过中间类,这2种情况直接aspect_swizzleForwardInvocation(klass); 就是__ASPECTS_ARE_BEING_CALLED__与 forwardInvocation进行方法交换。static NSString *const AspectsForwardInvocationSelectorName = @"__aspects_forwardInvocation:"; 这个AspectsForwardInvocationSelectorName对应原来的forwardInvocation的IMP
IMP originalImplementation = class_replaceMethod(klass, @selector(forwardInvocation:), (IMP)__ASPECTS_ARE_BEING_CALLED__, "v@:@");
if (originalImplementation) {class_addMethod(klass, NSSelectorFromString(AspectsForwardInvocationSelectorName), originalImplementation, "v@:@");}
如果没有hook过的对象,则创建中间类objc_allocateClassPair,objc_registerClassPair,aspect_hookedGetClass重定向class方法,使子类对象class方法返回base类对象,隐藏子类 aspect_hookedGetClass(subclass, statedClass) ,同时对forwardInvocation也进行方法交换
2.aspect_isMsgForwardIMP(targetMethodIMP) 判断原来selector 的IMP 是否是_objc_msgForward,如果没有,就新增一个带有AspectsMessagePrefix前缀的方法,并指向原seletor的IMP,原来seletor的imp指向!defined(__arm64__) ?_objc_msgForward :_objc_msgForward_stret这样最终方法都会走消息转发_objc_msgForward,最后会走到__ASPECTS_ARE_BEING_CALLED__
__ASPECTS_ARE_BEING_CALLED__的详细分析代码
1. AspectInfo *info = [[AspectInfo alloc] initWithInstance:self invocation:invocation];初始化一个AspectInfo对象,这个对象包含调用者和invocationaspect_invoke(aspects, info) 这个从AspectsContainer 当中的三个数组,去调度 这个info
具体核心调用代码如下:
1.用identirefier里的blocksign 去初始化一个调度者NSInvocation *blockInvocation = [NSInvocation invocationWithMethodSignature:self.blockSignature],从传入的info信息里面拿到NSInvocation *originalInvocation = info.originalInvocation,对比2个调度者参数信息,如果不一致返回NO,报错,然后从第二个参数开始遍历originalInvocation的参数传入到 blockInvocation,[originalInvocation getArgument:argBuf atIndex:idx];[blockInvocation setArgument:argBuf atIndex:idx];
最后设置blockInvocation的target,并invoke,[blockInvocation invokeWithTarget:self.block]这就是block的调用。依次执行classContainer.beforeAspects , classContainer.insteadAspects , classContainer.afterAspects重的block,如果是AspectPositionInstead的会替换aliasSelector的执行。
网友评论