Object-C對象收到消息後,究竟會調用何種方法需要在運行期才能解析出來。那也許就會問,與給定的選擇子名稱相對應的方法是不是也可以在運行期改變呢?答案是肯定的。若善用此特性,則可發揮巨大優勢,因爲我們既不需要源代碼,也不需要通過通過繼承子類來覆寫方法就能改變這個類本身的功能。這樣一來,新功能將在本類的所有實例中生效,而不是僅限於覆寫了相關方法的那些子類實例。此方案經常成爲“方法調配”(Method Swizzling)。
(1)在運行期,可以向類中新增或替換選擇子所對應的方法實現
(2)使用另一份實現替換原來方法的實現,這叫“方法調配”,此技術可向原有實現中添加新功能
(3)不宜濫用
@implementation UIViewController (Logging)
+ (void)load {
swizzleMethod([self class], @selector(viewDidAppear:), @selector(swizzle_viewDidAppear:));
}
- (void)swizzle_viewDidAppear:(BOOL)animated {
[self swizzle_viewDidAppear:animated]; //看着好像是遞歸,不過大家記住,這個方法是準備和viewDidAppear這兒方法互換的。所以,在運行期,swizzle_viewDidAppear選擇子實際上對應於原有的viewDidAppear方法的實現
NSLog(@"logging: %@", NSStringFromClass([self class]));
}
void swizzleMethod(Class class, SEL originalSelector, SEL swizzleSelector) {
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzleMethod = class_getInstanceMethod(class, swizzleSelector);
/*
class_addMethod,可以向類中動態的添加方法,用以處理選擇子。
嘗試添加原selector做一層保護,如果這個類沒有實現originalSelector,但其父類實現了,那麼class_getInstanceMethod方法返回的是父類的方法。這樣的話替換的就是父類的方法,不是我們希望的。所以嘗試添加originalSelector,如果已經存在,在用method_exchangeImplementations把原方法的實現和新方法的實現交換。
*/
BOOL success = class_addMethod(class, originalSelector, method_getImplementation(swizzleMethod), method_getTypeEncoding(swizzleMethod));
if (success) {
class_replaceMethod(class, swizzleSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzleMethod);
}
}
參考:http://tech.glowing.com/cn/method-swizzling-aop/
《52個有效方法》