研究版本官方最新版本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
中對方法進行替換實現對應功能。