什么是Aspects
Aspects是一个开源的的库,面向切面编程,它能允许你在每一个类和每一个实例中存在的方法里面加入任何代码。可以在方法执行之前或者之后执行,也可以替换掉原有的方法。通过Runtime消息转发实现Hook。Aspects会自动处理超类,比常规方法调用更容易使用。
其主要是面对对象的扩展,采用预编译的方式或者运行时的动态代理
核心
Runtime和Category的结合来实现
主要模块
共分为4个模块:
1.AspectIdentifier:对Runtime的封装,每次Hook都会保存对应的方法、Block等签名信息
2.AspectsContainer:容器类,根据枚举类型创建3个对应的数组:before、instead和after
3.AspectTracker:追踪一个类或对象的所有Hook操作,便于撤销
4.AspectInfo:保存有关NSInvocation的信息:sign、target等
核心流程
1.aspect_add
①初始化AspectIndetifier:主要用于保存IMP和要被替换的自定义Block,获取该Block的签名信息进行比对
②比较签名信息:如果Block的sign > SEL的sign,则继续比较;判断Block的sign > 1,默认参数是遵循了id<AspectInfo>协议的对象,判断是否是@<AspectInfo>;从参数的第二位开始遍历比较。
2.aspect_hookClass
记录被Hook的对象或类,生成一个子类(参照KVO原理)后调用aspect_swizzleClassInPlace,将forwardInvocation和自定义的函数进行绑定
方法流程
1.aspect_hookSelector
Aspects的主要接口
2.aspect_add
核心方法
3.aspect_isSelectorAllowedAndTrack
判断该方法是否允许Hook
①特定的方法和如retain、release和forwardInvocation无法Hook,将他们加入到持有的Blacklist中,在Blacklist中则直接return NO
②检查特定方法的顺序,如果Hookd额方法是dealloc,则位置必须是AspectPositionBefore,否则return NO
③判断对象或类对象是否能响应传入的方法,不能则return NO
④判断传入的方法是否是类方法(类方法只能Hook一次),是类方法则进行重复检查,存在则return NO
4.aspect_getContainerForObject
使用Runtime创建一个AspectContainer对象,拼接方法字符串放入容器中
5.identifierWithSelector:object:options:block:error:
创建AspectIdentifier对象
5.1.内部先调用aspect_blockMethodSignature
获取签名信息
①先将Block强转成相同格式的结构体,获取到信息
②判断Block是否含有签名,没有则return nil
③根据偏移量获取Block的参数信息,如果含有外部变量,则需要偏移两个void *
④成功获取到Block的参数信息并return NSMethodSignature
5.2.aspect_isCompatibleBlockSignature
比对传入的SEL和Block
①将传入的SEL转换成方法签名
②判断Block和SEL的参数个数,如果block签名.numberOfArguments > 方法签名.numberOfArguments return NO
③如果block签名的参数数量大于1(block的第0个参数是@?,代表是block),则判断第一个参数是否是_cmd(self,隐藏的参数),对应的type是@,不等于@则不匹配
④从第二个参数开始遍历匹配,全部成功则返回YES
6.addAspect:withOptions:
根据options,将AspectIdentifier对象加入到对应数组中
7.aspect_prepareClassAndHookSelector
开始Hook类和方法
7.1.aspect_hookClass
动态创建继承于该类的子类并替换
①获取类对象和类的isa指针
②判断是否包含前缀,如果包含,则代表Hook过了
③如果不包含前缀,再判断是否是元类,如果是元类则调用aspect_swizzleClassInPlace
④如果不包含前缀,也不是元类,再判断statedClass与baseClass是否相等,如果不相等,说明被KVO过,因为KVO对象的isa指针指向了另一个中间类,调用aspect_swizzleClassInPlace进行替换
⑤如果不是元类,也不是被KVO过的类,也没有被hook过,就继续向下执行,创建一个子类
⑥拼接子类类名,根据类名获取类对象
⑦如果类对象为nil,则使用Runtime创建一个继承于该类的子类
⑧调用aspect_swizzleForwardInvocation:将子类中的forwardInvocation方法替换为_ASPECTS_ARE_BEING_CALLED
⑨调用aspect_hookedGetClass方法将子类的isa指针指向statedClass
⑩调用aspect_hookedGetClass方法,将子类的元类isa指针也指向statedClass
⑪调用objc_registerClassPair注册刚刚新建的子类,再调用object_setClass方法,将self的isa指针指向新建的子类
7.2. aspect_swizzleClassInPlace
该方法中包含aspect_swizzleForwardInvocation
结论
Aspects主要是通过创建子类的方法,将指定的方法添加或将其替换为自定义的Block的第三方框架