2017.03.20 15:48*字數(shù) 583閱讀 78評論 4喜歡 5
這個 Tip 來源于一道面試題,感覺很是考察知識變通的能力,對 KVO 深入了解的同學聘惦,應該很容易就可以答出來。這里拋磚引玉示损,簡單聊聊這個 Tip
首先簡單總結下 KVO 的大概原理
當你觀察一個對象時朗和,會動態(tài)的創(chuàng)建該對象類的子類,這個子類重寫了被觀察屬性的 setter 方法谊娇,同時將該對象的 isa 指針指向了新創(chuàng)建的子類肺孤。在 Objective-C 中對象是通過 isa 指針來查找對應類中的方法列表的,所以這里可以把該對象看為新子類的實例對象济欢。重寫的 setter 方法會在調(diào)用原 setter 方法之后赠堵,通知觀察者對象屬性值的更改。
好的法褥,下面進入正題茫叭,聊聊如何為一個實例動態(tài)替換方法。
首先創(chuàng)建一個初始化工程半等,直接對 ViewController 進行實戰(zhàn)即可杂靶,在 ViewController 中加入一個 eat 方法,如下
- (void)viewDidLoad {? ? [superviewDidLoad];self.view.backgroundColor = [UIColorredColor];}- (void)eat {NSLog(@"original eat");}
然后寫一個 NSObject 的 Category 負責進行方法交換酱鸭,將原對象的 isa 指針指向該對象類的子類(LDSubclass)吗垮,并在子類中重寫 eat 方法
@implementationNSObject (Hook)+ (void)hookWithInstance:(id)instancemethod:(SEL)selector{
Method originMethod = class_getInstanceMethod([instance class], selector);
if (!originMethod) {
// exception ..
}ClassnewClass = [LDSubclassclass];// 修改 isa 指針的指向object_setClass(instance, newClass);}@end
子類的代碼很簡單,就是重寫 eat 方法凹髓,如果有需要烁登,可以調(diào)用原方法的實現(xiàn)
@implementationLDSubclass- (void)eat {? ? NSLog(@"newSubClass eat");? ? struct objc_supersuperClazz = {? ? ? ? .receiver = self,? ? ? ? .super_class=class_getSuperclass(object_getClass(self))? ? };// 調(diào)用原方法void(*objc_msgSendSuperCasted)(void*, SEL) = (void*)objc_msgSendSuper;? ? objc_msgSendSuperCasted(&superClazz,_cmd);}@end
最后在 ViewControlller 中進行測試即可,此時的 ViewController 代碼如下
@implementationViewController- (void)viewDidLoad {? ? [superviewDidLoad];self.view.backgroundColor = [UIColorredColor];? ? ViewController * vc = [[ViewController alloc] init];? ? [vc eat];NSLog(@"-----------");? ? ViewController * hookedInstance= [[ViewController alloc] init];? ? [ViewController hookWithInstance:hookedInstance method:@selector(eat)];? ? [hookedInstance eat];}- (void)eat {NSLog(@"original eat");}@end
來看看打印的結果蔚舀,第一個沒有 Hook 的實例饵沧,正常執(zhí)行;第二個Hook 后的實例赌躺,先執(zhí)行重寫的方法狼牺,后執(zhí)行原方法。
2017-03-2014:30:21.244JYHookInstanceMethod[91153:3422584]originaleat2017-03-2014:30:21.244JYHookInstanceMethod[91153:3422584]-----------2017-03-2014:30:21.245JYHookInstanceMethod[91153:3422584]newSubClasseat2017-03-2014:30:21.245JYHookInstanceMethod[91153:3422584]originaleat
原理
與 KVO 的 isa-swizzling 思路相同礼患,對想要 Hook 實例的類創(chuàng)建一個子類是钥,并在子類中重寫想要 Hook 的方法掠归,將該實例的 isa 指針指向子類,這樣進行方法查找時悄泥,便會在子類方法列表進行查找虏冻,如果想要執(zhí)行更多操作,可以在替換后的新方法中加入自己的邏輯弹囚。
這里只是一個超級簡單的 Demo厨相,很多邊界情況沒有考慮,后期可以自己完善鸥鹉,Demo 可以參考JYHookInstanceMethod