Aspects源码解析打算分两篇文章讲解
一个是解析,第二个就是作者为什么这么做。
本篇为源码流程分析
源码连接Aspects
Aspects源码解析(一)
Aspects源码解析之十万个为什么(二)
流程:
在替换A的foo方法
-
生成
A的子类B,类名为A__Aspects_ -
替换B的
forwardInvocation:的实现为__ASPECTS_ARE_BEING_CALLED__ -
替换B的
-(Class) class和+ (Class) class的实现方法,替换返回为A class -
为B添加方法名为:
foo_aspects_,实现方法为foo的方法实现、并把源方法foo的实现替换成_objc_msgForward -
当hook后,调用foo方法时候,跳转到
__ASPECTS_ARE_BEING_CALLED__去处理
Foo->_objc_msgForward->__ASPECTS_ARE_BEING_CALLED__
主要成员:
AspectsContainer(存储所有切片 object/class)
通过关联对象绑定到self ,key为aspects__foo
NSArray<AspectIdentifier *> *beforeAspects
NSArray<AspectIdentifier *> *insteadAspects
NSArray<AspectIdentifier *> *afterAspects
AspectIdentifier
存储block签名,判断是参数个数是否超过原方法
根据option添加到AspectContainner中
执行 [blockInvocation invokeWithTarget:``**self**``.block];
AspectTracker
(标记已经hook过的类) 存在单例swizzledClassesDict中key为class
AspectInfo(回调组装存储信息)
包含原始方法的签名和实例,回调使用存储参数
流程图&引用关系
细节实现(值得学习的地方):
-
通过不同block的flags来找到block的签名
signature- 了解block结构体组成,细节操作
-
disallowedSelectorList为什么是单例
- 黑名单列表针对的是全局,如果是单例不必每次都执行都创建,性能更高
-
枚举位运算
- 用于过滤
-
为什么替换
forwardInvocation- 消息发送
+ (BOOL)resolveClassMethod:(SEL)sel OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0); - (id)forwardingTargetForSelector:(SEL)aSelector OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0); - (void)forwardInvocation:(NSInvocation *)anInvocation OBJC_SWIFT_UNAVAILABLE("");- 直接调用和更适合调用
-
hooked检查:
- 父类/子类是否已经被替换,是否已经替换过(循环调用)
- block参数长度
-
白名单为什么使用单例。为什么设置白名单
- 避免错误和不合逻辑
- 因为整体重复调用,重复生成,浪费性能
-
使用类似kvo的结构的好处 class方法的hook
- 避免干扰原有class实现
-
为什么返回是id
- 屏蔽细节。给你想的东西
备注:
官方签名Type Encodings文档