Objective-C Runtime (三):Method Swizzling(方法替换)
Objective-C Runtime (三):Method Swizzling(方法替換)
Method Swizzling是一種改變改變一個'selector'的實際實現的技術。通過這一技術,我們可以在運行時通過修改類的分發表中selector對應的函數,來修改方法的實現。 實現圖解如下圖:
從上圖中,我們可以看到,使用Method Swizzling本質上是將selectorC的方法實現IMPc與selectorN的方法實現IMPn交換了,當我們調用selectorC,也就是給對象發送selectorC消息時,所查找到的對應的方法實現就是IMPn而不是IMPc了。那Method Swizzling在什么情況下可以用到了? 例如:我們接到一個需求:對 App 的用戶行為進行追蹤和分析。簡單來說,就是,就是當用戶進入某個界面或者點擊某個按鈕時,記錄這個事件。
最粗暴的方式,就是在每個 viewDidAppear 里添加記錄事件的代碼。這種方式缺點是很明顯的,它破壞了代碼的干凈整潔。因為記錄事件的代碼本身不屬于原有代碼的主要邏輯。隨著項目擴大、代碼增加,我們的原有代碼里會到處分布著記錄事件的代碼。這時,要找到一段事件記錄的代碼會變得困難,也很容易忘記添加事件記錄的代碼。
我們可能會想到使用繼承或類別,在重寫的方法里添加事件記錄的代碼。但這樣也會帶來新的問題:
我了解決以上的問題,我們可以使用Method Swizzling,如以下代碼所示:
#import "UIViewController+Tracking.h" #import <objc/runtime.h> @implementation UIViewController (Tracking)+ (void)load {static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{Class class = [self class];// When swizzling a class method, use the following:// Class class = object_getClass((id)self);SEL originalSelector = @selector(viewWillAppear:);SEL swizzledSelector = @selector(track_viewWillAppear:);Method originalMethod = class_getInstanceMethod(class, originalSelector);Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);BOOL didAddMethod =class_addMethod(class,originalSelector,method_getImplementation(swizzledMethod),method_getTypeEncoding(swizzledMethod));if (didAddMethod) {class_replaceMethod(class,swizzledSelector,method_getImplementation(originalMethod),method_getTypeEncoding(originalMethod));} else {method_exchangeImplementations(originalMethod, swizzledMethod);}});}- (void)track_viewWillAppear:(BOOL)animated {[self track_viewWillAppear:animated];NSLog(@"viewWillAppear: %@", self); }@end 復制代碼從上面代碼可以看出,我們通過method swizzling修改了UIViewController的@selector(viewWillAppear:)對應的函數指針,使其實現指向了我們自定義的track_viewWillAppear:的實現。這樣,當UIViewController及其子類的對象調用viewWillAppear時,都會打印一條日志信息。
上面代碼需要解釋的問題: class_addMethod:要先嘗試添加原 selector 是為了做一層保護,因為如果這個類沒有實現 originalSelector ,但其父類實現了,那 class_getInstanceMethod 會返回父類的方法。這樣 method_exchangeImplementations 替換的是父類的那個方法,這當然不是你想要的。所以我們先嘗試添加 orginalSelector ,如果已經存在,再用 method_exchangeImplementations 把原方法的實現跟新的方法實現給交換掉。
注意事項 Swizzling通常被稱作是一種黑魔法,容易產生不可預知的行為和無法預見的后果。濫用可能會造成很多問題,如果遵從以下幾點預防措施的話,還是比較安全的:
參考:
總結
以上是生活随笔為你收集整理的Objective-C Runtime (三):Method Swizzling(方法替换)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 算法常用思想
- 下一篇: 如何对DevExpress ASPxGr