method swizzling也許是runtime中最有爭議的技術,它的做用就是改變已經存在selector的實現,之因此能夠這樣是由於方法調用能夠在運行時改變:經過改變類的分發表( dispatch table,該表包含selector的名稱及對應實現函數的地址)裏selector和實現之間的對應關係。
舉個例子,好比你想記錄一個iOS應用裏每一個view controller顯示的次數:能夠在每一個view controller添加記錄的代碼,但這會致使大量的重複代碼;經過繼承也是一個方法,但須要同時建立UIViewController, UITableViewController, UINavigationController及其它中view controller的子類,一樣也會產生許多重複的代碼出現。
幸運的是,在UIViewController的category使用method swizzling:html
#import <objc/runtime.h> @implementation UIViewController (Tracking) + (void)load { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ Class class = [self class]; SEL originalSelector = @selector(viewWillAppear:); SEL swizzledSelector = @selector(xxx_viewWillAppear:); Method originalMethod = class_getInstanceMethod(class, originalSelector); Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector); // 若是 swizzling 的是類方法, 採用以下的方式: // Class class = object_getClass((id)self); // ... // Method originalMethod = class_getClassMethod(class, originalSelector); // Method swizzledMethod = class_getClassMethod(class, swizzledSelector); //交換實現 method_exchangeImplementations(originalMethod, swizzledMethod); }); } #pragma mark - Method Swizzling - (void)xxx_viewWillAppear:(BOOL)animated { [self xxx_viewWillAppear:animated]; NSLog(@"viewWillAppear: %@", self); } @end
如今,當一個UIViewController或者其子類的實例調用viewWillAppear:方法時,就會打印出一條記錄。假如要在在view controller的生命週期,view的繪製或者Foundation的網絡協議棧注入一些自定的行爲,method swizzling也許是你應該考慮的一個方向。objective-c
下面是使用method swizzling應該注意的點:安全
oc會在運行時自動調用每一個類的兩個方法,+load 會在類初始化加載的時候調用;+initialize方法會在程序調用類的第一個實例或者類方法的時候調用。這兩個方法都是可選的,只會在實現的時候纔去調用。因爲method swizzling會影響到全局的狀態,所以最小化競爭條件的出現變得很重要,+load方法可以確保在類的初始化時候調用,這可以保證改變應用行爲的一致性,而+initialize在執行時並不提供這種保證,實際上,若是沒有直接給這個類發送消息,該方法可能都不會調用到。網絡
如上,因爲swizzling會改變全局狀態,因此咱們須要在運行時採起一些預防措施。原子性就是其中的一種預防措施,由於它能保證無論有多少個線程,代碼只會執行一次。GCD的dispatch_once 可以知足這種需求,所以在method swizzling應該將其做爲最佳的實踐方式。app
在oc中,選擇器、方法和實現是運行時的特殊方面,雖然在通常狀況下,這些術語是用在消息發送的過程當中。
下面是Apple對它們的幾個描述:函數
- (void)xxx_viewWillAppear:(BOOL)animated { [self xxx_viewWillAppear:animated]; NSLog(@"viewWillAppear: %@", NSStringFromClass([self class])); }
可奇怪的是,它並不會。在swizzling的過程當中,xxx_viewWillAppear:已經被從新指向UIViewController 的原始實現-viewWillAppear:,可是若是咱們在這個方法中調用viewWillAppear:則會致使無限循環。學習
一般認爲Swizzling是一個比較危險的技術,容易產生不可預料的行爲和沒法預見的後果,但只要遵循如下幾個注意事項,其實method swizzlin仍是相對安全的。ui