组件化分发生命周期 方案实现探索系列文章:
1、组件化分发生命周期
2、组件化分发生命周期 - AOP 方案
3、组件化分发生命周期 - AOP 方案(libffi 实现)
在之前的文章( 组件化分发生命周期 )里已经介绍过了 组件化分发生命周期 的概念及理由,并总结了几种分发生命周期的方案,最后一节的方案本以为已经很完美了,但是在项目中实际应用的时候还是发现了问题。
问题
因为我们工程里用了 Firebase 打点,其中 Firebase 依赖的一个库 GoogleUtilities
也对 UIApplication 的 delegate 里的一些方法做了 hook 操作,对 APP 做了一些监听操作。以下是 GoogleUtilities
产生影响的关键代码:
1 | + (GULApplication *)sharedApplication { |
其中 kGULApplicationClassName
就是 @"UIApplication"
。因为在上一种方案里我们将系统的 delegate 重设成了我们自己的类:GHLApplicationDelegate
,并通过消息转发的方式统一处理了原 AppDelegate
里的所有方法。
这样一来,Firebase 通过以上代码得到的 GULApplication
就变成了 GHLApplicationDelegate
,再去 hook application:didFinishLaunchingWithOptions:
等方法的时候就找不到这些方法了,即 Firebase 失去了相应的功能。
这里是 Firebase 的场景,其他第三方库有相应 hook 操作的话也会有相同的问题,而我们在实际开发过程中没法避免其他库的 hook 操作,甚至可能察觉不到问题的存在,所以之前提到的方案是有问题的。
分析
这样一想,原本的方法的确存在设计上的问题,我们只顾着自己的 hook,影响了其他第三方对 AppDelegate
的 hook 操作,这显然不合理。
为了解决以上的问题,不得不再次寻找方案。重新梳理下我们的需求:
在主工程的 AppDelegate
里执行 UIApplicationDelegate
的一系列生命周期的方法的时候,相应的各个 Pod 里也能有对应方法同时被执行,且不影响其他第三方对 AppDelegate
的 hook 操作。例如:主工程 AppDelegate
里的 application:didFinishLaunchingWithOptions:
方法被执行时,各个 Pod 里也能有对应的 application:didFinishLaunchingWithOptions:
方法被执行。
那我们也可以 hook AppDelegate
的各阶段方法,遍历执行各个 Pod 里的对应方法之后,再执行下原本的方法。这样大家都保证各自的 hook 链不会断掉就 ok 了。
解决方案:AOP
想起 Aspects 这个库。或许,AOP 是一种不错的解决方案。思路如下:
1)首先还是采用上一篇文章(组件化分发生命周期)的方案,在各个 Pod 里通过继承 HoloLifecycle
类的方式让其子类拥有执行 UIApplicationDelegate 生命周期方法的能力。
2)hook UIApplication
的 setDelegate:
方法,拿到 AppDelegate
3)遍历 AppDelegate
的方法列表,判断是否是 UIApplicationDelegate
的协议方法
4)使用 AOP 的方法,对以上遍历加判断筛选拿到的方法一一进行 hook
5)在被 hook 的每个方法执行前,遍历 HoloLifecycle
的所有子类,执行同名方法
以上就是根据需求想到的解决步骤,唯一的关键就在最后一步,执行 HoloLifecycle
类里的方法时,因为入参不确定,performSelector:withObject:
方法肯定是不适用了,可以用 NSInvocation
的方法转发消息,参考 Aspects
。
大体思路就是:
1)通过 Aspects
hook AppDelegate
方法,让被 hook 的方法不会被正常执行,而是走消息转发流程(Aspects
的思路)
2)在消息转发的最后一步 forwardInvocation:
方法里获取当前所 hook 方法的 NSInvocation
对象 A,在这个对象里存储着转发的方法名,参数值等信息
3)根据 HoloLifecycle
类对象(target)和想要分发的方法的方法名(selector),创建一个 NSInvocation
对象 B
4)遍历 NSInvocation
对象 A 的所有参数,把它们依次赋值给 NSInvocation
对象 B,并发送对象 B
遍历所有的 HoloLifecycle
类对象,循环执行以上步骤就可以了,这样就做到了 hook AppDelegate
各生命周期方法,在各方法执行前遍历执行 HoloLifecycle
类对象的对应同名方法,也就是:组件化分发生命周期 的最终效果。
一开始以为 Aspects
暴露的方法里没法获取所 hook 方法的 NSInvocation
,所以花了些力气参考 Aspects
的源码实现了一套方案。能够正常工作之后才发现 Aspects
的 hook 方法的 block 里可以拿到 AspectInfo
这个对象,而这个对象持有的 originalInvocation
属性,就是所 hook 方法走 forwardInvocation:
方法时拿到的 NSInvocation
。所以,最后借助 Aspects
的实现简单了很多,Aspects
已经完成了 hook 和返回 NSInvocation
的功能,我们主要完成以上 3)、4)步骤即可,以下是关键代码:
1 | @implementation UIApplication (HoloLifecycle) |
补充功能
1、参考青木的 TDFModuleKit,也给 HoloLifecycle
类添加了优先级方法,在子类里重写该方法返回合适的优先级即可决定执行顺序,
并且 HoloLifecycle
支持在 AppDelegate
方法之前、之后两阶段执行:
1 | /// 调用优先级 |
2、使用 HoloLifecycleManager
类的以下方法可以添加 log 功能,方便监控各个方法的调用顺序和耗时:
1 | /// 打印所有 HoloLifecycle 子类执行方法及耗时 |
代码已开源至:HoloLifecycle,欢迎使用。