Aspects的源码的总体分析--切面编程

一、我们直接从接口的入口开始分析:

Aspects的源码的总体分析--切面编程_第1张图片

aspect_performLocked:线程安全处理

aspect_isSelectorAllowedAndTrack:进行参数的校验

AspectsContainer*aspectContainer = aspect_getContainerForObject(self,

selector):得到已有或创建新的AspectsContainer,现在你只需知道有这个对象就行了,后面用到再分析

identifier = [AspectIdentifier identifierWithSelector:selector object:self options:options block:blockerror:error]:这个对象才是主要的,跟很多操作都有关系,上面的AspectsContainer主要也是为了得到它。也是一样,后面用到再分析。

二、aspect_prepareClassAndHookSelector:开始了神奇的hook操作,这函数不得了。我们进去看看作者怎么hook的。

Aspects的源码的总体分析--切面编程_第2张图片

幸好代码也不多,遇到代码较多的函数,我们先不进去细看,可以先靠猜。

Classklass = aspect_hookClass(self, error):返回一个类,我们可以大概猜到,这个类肯定跟原类有关,那么就先认为它是原类的子类吧!

Method targetMethod = class_getInstanceMethod(klass, selector);

IMP targetMethodIMP = method_getImplementation(targetMethod):

获得selector对应的方法实现IMP

if (!aspect_isMsgForwardIMP(targetMethodIMP)):这函数简单,进去看看,就是判断impl == _objc_msgForward是否相等,_objc_msgForward一个没实现的函数。我就叫它空函数吧!

下面的,直接看注释吧,拷贝代码对齐好累,不一一分析了。这段代码总的意思是:

1、通过一个新的selector(aspect_aliasForSelector返回一个重新命名的SEL)关联原来的函数实现IMP。即是保存了原来了函数的IMP,为了之后可以调用,因为下面我们要修改它的selector,如果不重新指向,就再也找不到了。

2、把原来的selector关联到上面提到的空函数,原来的函数重新指向了,但指向的是空。

就这样,那么问题来了,原来的selector执行了空函数,就是说,调用后,就会因为没有实现,便会做以下的求救处理:

resolveInstanceMethod(NO)—>forwardingTargetForSelector(nil或self)—-> (methodSignatureForSelector返回NSMethodSignature)forwardInvocation

有三个补救方法,其中源码用的是forwardInvocation来补救。就是说hooks的就是forwardInvocation函数。

forwardInvocation做了什么呢?我们可以根据上下文推导下:

如果配置的是before,就是在调用原来方法前调用我们的block。

1、那么就是先调用我们的block

2、再根据之前保存的原函数aliasSelector,调用原来的函数。

你带着这个想法去看aspect_hookClass的实现,会简单点。


三、aspect_hookClass函数分析:

Aspects的源码的总体分析--切面编程_第3张图片

上面的方法构建一个子类。当然,如果传入的已经是类了,那么就不需要创建子类了。然后就hook forwardInvocation方法。

这里可以看到,我们之前的猜测是对的,确实是创建了原类的子类。

然后是:

aspect_swizzleForwardInvocation(subclass);

真正的hook操作在这里。hook forwardInvocation方法于__ASPECTS_ARE_BEING_CALLED__

Aspects的源码的总体分析--切面编程_第4张图片

把forwardInvocation指向了新的函数__ASPECTS_ARE_BEING_CALLED__

class_addMethod(klass, NSSelectorFromString(AspectsForwardInvocationSelectorName), originalImplementation, "v@:@"):这个是,如果被hook的方法原本已是通过forwardInvocation来处理,那么就保存原本这个forwardInvocation的实现,以便后面调用。这个不是重点。

想不到还要再进一层:__ASPECTS_ARE_BEING_CALLED__

Aspects的源码的总体分析--切面编程_第5张图片

还是以before做分析吧:

这里做到上面提到了两个推测:

1、先执行了指定的block :

aspect_invoke(objectContainer.beforeAspects, info);

2、再调用原来方法:

if ((respondsToAlias = [klass instancesRespondToSelector:aliasSelector])) {

                [invocationinvoke];

                break;

            }

AspectsContainer *objectContainer = objc_getAssociatedObject(self,aliasSelector);

拿到一开始创建的那个AspectsContainer,你可以看回最前面那里。它beforeAspects存的是之前的AspectIdentifier。

四、然后分析下是如何执行block的:

- (BOOL)invokeWithInfo:(id)info

Aspects的源码的总体分析--切面编程_第6张图片

执行block方法:[blockInvocationinvokeWithTarget:self.block];

它的第一个参数为:传入来的AspectInfo

[blockInvocationsetArgument:&info atIndex:1];

所以它的接口说只用一个参数(id)info就可以了,因为它的arguments属性存了正在的所有参数,转换为数组返回:

Aspects的源码的总体分析--切面编程_第7张图片

五、分析下前面的AspectIdentifier,既然那么重要

1、[AspectIdentifier identifierWithSelector:selector object:self options:optionsblock:block error:error];最开始的初始化:

Aspects的源码的总体分析--切面编程_第8张图片

NSMethodSignature *blockSignature = aspect_blockMethodSignature(block, error);

把block转为通过签名调用,所以才有了后面:

//block的NSInvocation

NSInvocation *blockInvocation = [NSInvocation invocationWithMethodSignature:self.blockSignature];

[blockInvocationinvokeWithTarget:self.block];

整体代码架构已分析完成,具体的东西你再细看。

六、整份代码还是有很多干货的:

1、函数还可以这样调用:

先获取函数签名:NSMethodSignature

再通过签名转化为:NSInvocation

最后通过NSInvocation设置参数,target,然后就invoke调用

2、block也可以转换为NSInvocation来调用,源码里转换过程还是有点神奇的,你去看看《OC高级这本书》block,获悉能理解转换过程。Block其实就是一个对象。

3、对类,元类等概念的理解

你可能感兴趣的:(Aspects的源码的总体分析--切面编程)