1. KVO的基础原理分析
略过, 相关内容很多
2. Aspects 的基础原理分析
本文不贴源码, 只梳理逻辑, 这里只分析实例的hook, 具体流程如下:
例如需要替换类Person的实例person的setName:方法, Aspects中处理的方式如下:
- 创建动态子类
Person_Aspects_, 它的superClass指向Person - 替换动态子类
Person_Aspects_的-(Class)class和+(Class)class, 类似KVO的isa隐藏实现, 返回Person的对应的SEL的IMP - 替换动态子类
Person_Aspects_的-(void)forwardInvocation:(NSInvocation *)anInvocationSEL对应的IMP实现为__ASPECTS_ARE_BEING_CALLED__ - 给动态子类
Person_Aspects_增加-setName_aspects_:,该SEL的IMP是-setName:的IMP, 同时将-setName:的IMP替换成_objc_msgForward - 修改person的isa指针指向
Person_Aspects类
此时调用, [person setName:xxx]方法时, 直接调用_objc_msgForward进入消息转发流程会调用Person_Aspects_的-forwardInvocation:方法, 底层IMP指针是__ASPECTS_ARE_BEING_CALLED__, 在这个实现中去进行AOP切面处理!!!
关键技术点:
- block的内部结构, 尤其Block的MethodSignature, isa处理!
- 封装
AspectIdentifier封装需要hook的selector,block,blockSignature,object等全部信息 - 使用
alias_selector构造Associated关联对象, 持有容器对象AspectContainer, 通过AspectContainer的容器持有AspectIdentifier void __ASPECTS_ARE_BEING_CALLED__(__unsafe_unretained NSObject *self, SEL selector, NSInvocation *invocation)方法中通过aliase_selector取出容器中的AspectIdentifier对象,调用invokeWithInfo的中触发hook的block!!!
其他技术点:
- 使用
dispatch_once单例构造全局黑名单 - oc消息查找与消息转发全流程
关于block的内部结构, 可以参考更多资源
libclosure-79
3. 神策数据的isa HOOK 与KVO兼容
神策的埋点服务中, 针对 setDelegate:方法进行hook, 其中核心原理结合 isa swizzling 和 method swizzling, 以及兼容 NSProxy/NSObject 对象作为delegate 的场景, 并通过对象是NSProxy/NSObject来决定兼容KVO的处理:
- 使用关联对象
sensorsdata_superClass标记是否已经被hook处理过 - 判断是否被系统KVO过, 如果进行过KVO, 那么此时对象的类型是
KVONotifying_WBTVDelegate- 获取realClass,
WBTVDelegate - 直接在
KVONotifying_WBTVDelegate类中添加方法removeObserver:forKeyPath:- hook的
removeObserver:forKeyPath:中, 移除Observer以后, 重新走1的流程, 重新HOOK
- hook的
- 并在
KVONotifying_WBTVDelegate类中添加方法hook 的 selector方法, 在hook的delegate方法中实现进行AOP打点
- 获取realClass,
- 创建动态子类增加前缀
__CN.SENSORSDATA.序号.WBTVDelegate - 在动态子类中增加需要hook selector, 并实现IMP, 在IMP中打点
- 判断delegate object是否NSObject,
- 如果是, 那么需要兼容KVO
- 在
__CN.SENSORSDATA.序号.WBTVDelegate中增加addObserver:forKeyPath:options:context:实现 - 在
addObserver:forKeyPath:options:context:中先调用原系统KVO, 然后重写__CN.SENSORSDATA.序号.WBTVDelegate的-class方法, 隐藏系统KVO与动态子类
- 注册动态子类, 设置isa swizzling
- 新建一个关联对象
SADelegateProxyParasite设置给delegate object, 用来监听delegate object的dealloc调用, 此时调用disposeClass, 清理动态子类__CN.SENSORSDATA.序号.WBTVDelegate
神策这套isa swizzling实现, 可以兼容NSProxy代理链, 也能兼容 NSObject的KVO