阅读 710

iOS中的AOP(2)-SFAspect的实现原理

概述

上一篇文章提及到了AOP的概念和一些应用例子,以及SFAspect是基于消息转发实现AOP的,这一篇文章主要是讲述SFAspect的实现原理

AOP实现原理

  • 前置知识:runtime
  • 如何利用runtime进行hook
  • 执行hook的操作
  • hook的撤销和类的销毁
  • 其他细节

runtime

SFAspect是基于runtime中的消息转发去实现的,因为runtime的知识点涉及到比较多,这里放在另外的文章去分析

  • rumtime概览
  • 消息发送和消息转发机制

iOS runtime中说明了像OC中面向对象的基础,和消息发送机制的概述流程

objc库源码分析(3)-方法调用-消息发送objc库源码分析(3)-方法调用-消息转发 中,从objc源码中详细分析了消息发送和消息转发的详细流程


如何利用runtime进行hook

Objective-c中的函数调用其实是转换成objc_msgSend系列的函数调用,也就是消息发送,

调用objc_msgSend()时会传入接受对象target和方法名称sel以及参数。在objc_msgSend函数回去查找方法缓存,看类的缓存是否存在方法sel对应的方法实现IMP。如果找不到,就会执行lookUpImpOrForward()方法去查找sel对应的IMP.

在lookUpImpOrForward()中会去查找方法的IMP,如果找不到,就会进入到动态解析过程,如果还是找不到方法实现,就会将该sel对应的IMP设置为_objc_msgForward,并存入到方法的缓存中。将IMP设置为forward_imp,也就是说方法调用即将进入消息转发流程。

消息转发过程中有两个步骤 1 查找替代执行的对象,也就会调用forwardingTargetForSelector方法 2 如果找不到替代执行的对象,则会内部消化,调用methodSignatureForSelector获取方法签名和调用forwardInvocation执行自定义操作

SFAspect的实现正是通过将被调用方法的sel对应的IMP设置为_objc_msgForward,让调用方法进入消息转发流程,并在自定义的methodSignatureForSelector和forwardInvocation中进行自定义的操作去实现AOP功能。如下图

通过方法交换的方法去实现为类添加自定义methodSignatureForSelector和forwardInvocation方法,然后在自定义的forwardInvocation执行我们需要的操作,去实现OC中的Hook。


执行hook的操作

上面提到,会在自定义的forwardInvocation执行我们需要的操作,那如何在forwardInvocation中执行自定义的操作呢?

  1. 封装

首先调用hookSel或hookAllClassSel的时候,我们会把hookSel和hookAllClassSel里面的内容(被hook的对象,hook的ID,优先级,sel以及操作block)包装成一个SFAspectModel对象

  1. 绑定

将SFAspectModel对象添加到被hook对象的SFAspectContainer中,SFAspectContainer 是一个管理被hook对象所有的hook内容的容器类。SFAspectContainer是通过动态绑定objc_setAssociatedObject绑定到对象的类中(如果对象是实例,则绑定到类中,如果对象是类,则绑定到类对应的元类中)

  1. 操作

当调用hook方法的时候,进入到消息转发流程,执行自定义的forwardInvocation的时候。在自定义的forwardInvocation中取出被hook对象的SFAspectContainer容器,拿到容器中的SFAspectModel对象,取出SFAspectModel对象的操作block

上面的三个步骤关系如下图所示

通过强制使方法调用进入消息转发和上述操作步骤后,就可以实现Hook一个方法了。

在SFAspect中可以通过hookAllSel去hook一个类所有实例的对象,也可以通过hookSel去hook一个对象。

  • 对于hook一个类所有实例的对象,是在对象的类中去进行图中的步骤
  • 对于hook一个对象,是通过动态生成一个子类,再将对象的isa指针指向新建的子类(和KVO的实现方式一致),然后在子类中进行上图的操作

hook的撤销后续操作和类的还原

  • 停止后续操作

SFAspect中可以通过调用SFAspectModel的stop方法停止后续操作,其实实现的方式也特别简单,就是通过抛出异常去实现的。当我们对方法进行hook的时候,没进行一个hook操作都会用try catch去捕获异常,如下图所示

在调用stop函数时,就会抛出一个Error,当catch到这个error的时候,就会退出forwardInvocation函数

  • 关于类的还原

当我们调用removeHook方法的时候,会在对象的类的SFAspectContainer中检查是否有该sel的hook对象SFAspectModel,如果有,则删除。 当SFAspectContainer为空的时候,就会销毁SFAspectContainer。并同时重置被hook的方法,methodSignatureForSelector和forwardInvocation方法,


其他细节

文章中有些细节没有提价到,如hook优先级的控制,动态生成子类的释放,锁操作等 代码不多,如果感兴趣的童鞋可以下载源码看一下

文章分类
iOS
文章标签