研究版本官方最新版本Release 1.4.2git
先KVO再Hook
其setter函数后,调用obj的setter函数奔溃。先Hook再KVO
,则正常运行。1.先对比两种添加顺序,致使的obj
的isa
和函数列表
的不一样。github
经过下面的函数打印出,当前的obj→isa
及其函数列表
。安全
NSLog(@"\nclass - %@\nclass methods - %@", object_getClass(self.testObj), [self allMethodsWithClass:object_getClass(self.testObj)]);
NSLog(@"\nclass - %@\nclass methods - %@", self.testObj.class, [self allMethodsWithClass:self.testObj.class]);
复制代码
打印结果去除时间戳信息,显示以下bash
先KVO再进行Hook 函数
先hook再进行KVO 优化
对比能够发现,若是先KVO再hook
,KVO会先动态生成一个NSKVONotifying_
前缀的子类,而且会重写setName:
,添加class
等函数,将class
的返回结果指向子类。 Aspect
在hook的时候,若是发现obj.class
和object_getClass(obj)
不相等的话(正常发生在对象被KVO重写过isa),则不会动态地再创建一个子类,而直接会原地hook,添加aspects__setName:
和forwardInvocation:
,交换aspects__setName:
和setName:
的函数实现。(此时交换的是已经被KVO过的NSKVONotifying_AKObject
的setName:
函数实现)spa
此时对name
进行赋值的时候,对obj
调用setName:
,在Aspect
中若是不是使用指定AspectPositionInstead
的option替换原方法实现,则会走原方法实现,则将对obj
调用交换后的方法名aspects__setName:
(对应原方法实现),NSKVONotifying_AKObject
在处理aspects__setName:
时候会调用其子类的方法,可是并不会保存子类原来的setter
的方法名,而是直接讲本身当前的selector(_cmd)
当作方法名,并对子类AKObject进行调用,可是子类中并无实现aspects__setName:
,因此产生崩溃。3d
对于先hook再进行KVO
的状况,setName:
原类及其两个生成的子类都有对应的方法实现,因此能够正常响应。code
PR地址:github.com/steipete/As…cdn
git地址:github.com/doggy/Aspec…
这个PR创造性的在调用原函数实现
那一步骤上作了优化,要调用原函数实现时,先交换回原始函数名及原始的函数实现
,再对obj
调用原函数名
,此时在NSKVONotifying_AKObject
的setName:
中拿到的_cmd
就是原始的selector
,子类中也有对应的方法实现,一切都能正常运行。而后再把invocation的selector
及对应子类中的方法实现
交换回去,完成对原始函数实现的调用。
1.先hook再KVO
,若是在清除KVO前对AspectToken进行remove,崩溃。
id token = [object aspect_hookSelector...];// add hook
[object addObserver...]; // add KVO
[token remove]; // crash
复制代码
缘由:在先hook再KVO
这过程当中,obj.isa
经历的变化为
NSObject
→ NSObject_Aspects_
→ NSKVONotifying_NSObject_Aspects_
假如在移除KVO前移除Aspects的hook,Aspects须要把当前子类还原为原始子类,
Aspects的处理逻辑是去除_Aspects_
后缀,并将isa
指向去除后缀后的类,
那isa
的变化过程为
NSKVONotifying_NSObject_Aspects_
→ NSKVONotifying_NSObject
可是,此时程序中并无存在NSKVONotifying_NSObject
这个类,所以程序会崩溃。(这个坑在最新版的Aspects中仍然存在,且暂时无解决方法)
2.若是是先KVO再Hook
的状况,要注意在obj
的dealloc
时才能remove全部观察者,若是提早remove掉全部观察者,hook是对NSKVONotifying_NSObject
的方法hook,但移除全部观察者后就会把对应的NSKVONotifying_NSObject
类销毁,此时isa
NSKVONotifying_NSObject
→ NSObject
原有的hook将会失效
那么,咱们应该如何正确且安全地使用同时使用Aspects和KVO呢?
先KVO再Hook
这种状况下,hook的有效期。+(void)load
中对方法进行替换实现对应功能。