iOS AOP编程

93 阅读3分钟

一、简介

AOP(面向切面编程,Aspect-Oriented Programming)是一种编程范式,旨在提高代码的模块化和可维护性。

它通过把不同模块的共同关注点抽离出来,实现对核心业务的解耦。并且在逆向工程里,常常用于改变原程序的执行流程。

  • 不同模块的共同关注点例如:日志记录,安全性(防崩溃)、事务管理等

二、iOS实现方式

AOP通常使用Hook技术实现,而hook的实现方式常见有两种:

  1. Method Swizzling (方法交换)
    1. 交换OC函数(最常见)
    2. 交换系统的C函数,使用fishhook
    3. 交换自己的C函数,叫InlineHook,代表框架:Dobby
  2. 消息转发,代表性框架:Aspects
  • 分类:可以统一拓展类的方法,它能实现部分AOP的功能,但是不完全等同于AOP

三、原理分析

  1. Method Swizzling(方法交换)

    1. 使用runtime的API,根据SEL取出各自的IMP,然后交换IMP的指向
    2. 注意:如果要在原来的方法执行前或者后做一些操作,那么在我们自定义的那个方法里面做完了我们自己的操作之后,要调用一下这个方法自身,因为这个方法的IMP已替换成了原来方法的IMP,不然原来的功能会丢失
    3. 方法交换核心代码:
  2. 消息转发

    1. 常用框架:Aspects
      • Aspects是在调用原来方法时,直接让消息传递的流程直接进入到消息转发的流程,而且是直接到消息转发的最后一步:forwardInvocation,把带有我们自定义相关逻辑的Block,在这个函数里面执行,下面流程图
      • Aspects 会将目标 selector 的实现设置为 Aspects 提供的 aspect_getMsgForwardIMP 方法的返回值。aspects_getMsgForwardIMP 的返回值本质上是一个能够直接触发消息转发机制的方法。更加特殊的地方在于,这里会直接进入消息转发的最后一步 forwardInvocation:。
      • 它能保证使用这个框架hook的同一个方法只被hook一次
    2. Aspects可以对类的所有实例对象 和 单个实例对象 的方法插入我们自己的代码
    3. 但是Aspects这框架不支持修改类对象或者实例对象的类方法,这都一律无效,内部不支持。但是我们可以直接使用Method Swizzling来交换类方法
    4. 利用Aspect来Hook实例对象方法,会动态生成一个子类,原理和KVO一样,修改实例对象的isa指针指向,但是在方法插入时和KVO不一样,KVO是复写父类方法,AOP是利用消息转发时插入自定义方法

四、注意事项

  • 在开发中使用 AOP 来 hook 系统方法时需要谨慎,因为其他第三方框架可能也会对这些方法进行 hook,这样就会形成一个类似链条的调用过程:执行原方法时会先调用最新的 hook,然后依次调用之前的 hook,直到原方法本身。然而,如果某些 hook 选择直接替换原方法,而不调用原方法的 IMP,就可能导致链条断裂,从而引发后续方法无法执行,导致一些奇怪的问题。