iOS runtime學習之Method Swizzling(方法調配技術)

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個有效方法》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章