Method Swizzling

Swizzling 的應(yīng)用場景:
1)比如需要統(tǒng)計在每個界面停留時間。我們需要在

  • (void)viewWillAppear:(BOOL)animated;
  • (void)viewDidAppear:(BOOL)animated;
    兩個方法中寫上統(tǒng)計用的方法,但是按照常規(guī)的方法,需要在每個界面加上統(tǒng)計代碼,這樣不僅繁瑣而且很有可能落掉一些頁面(這個時候你可能想使用基類,但是很多項目并沒有繼承統(tǒng)一的一個或者幾個基類),所以這個時候就需要使用黑魔法 Swizzling。

2)還可以用來打印每個頁面的類名,這樣當(dāng)你接手一個新項目的時候,可以使用Swizzling來打印出每個控制器的名稱,這樣就很容易的弄清項目的架構(gòu)。

先看一段代碼:

#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);

        // When swizzling a class method, use the following:
        // Class class = object_getClass((id)self);
        // ...
        // Method originalMethod = class_getClassMethod(class, originalSelector);
        // Method swizzledMethod = class_getClassMethod(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);
        }
    });
}

#pragma mark - Method Swizzling

- (void)xxx_viewWillAppear:(BOOL)animated {
    [self xxx_viewWillAppear:animated];
    NSLog(@"viewWillAppear: %@", self);
}

@end

先來看代碼解釋:

1)+(void)load 和 +(void)initialize的區(qū)別

//Invoked whenever a class or category is added to the Objective-C runtime; implement this method to perform class-specific behavior upon loading.
//A class’s +load method is called after all of its superclasses’ +load methods.
//A category +load method is called after the class’s own +load method.
+(void)load
在Objective-C運行時載入類或者Category時被調(diào)用,這個方法對動態(tài)庫和靜態(tài)庫中的類或(Category)都有效.

//initialize is invoked only once per class. If you want to perform independent initialization for the class and for categories of the class, you should implement load methods.
+(void)initialize

+(void)initialize 會在運行時僅被觸發(fā)一次,如果沒有向類發(fā)送消息的話,這個方法將不會被調(diào)用。這個方法的調(diào)用是線程安全的。父類會比子類先收到此消息。

要點:
1、initialize和load,我們并不需要在這兩個方法的實現(xiàn)中使用super調(diào)用父類的方法。
2、load和initialize被調(diào)用一次是相對runtime而言 ,你可以當(dāng)作普通類方法多次調(diào)用。
3、類加載到系統(tǒng)的時候就用調(diào)用load方法,類首次使用的時候調(diào)用initialize方法。
4、load不像普通方法一樣遵從那套繼承規(guī)則,當(dāng)每個類沒有實現(xiàn) load方法,不管各級超類是否實現(xiàn),系統(tǒng)都不會調(diào)用此類的load方法。initialize與其他方法一樣,如果每個類沒有實現(xiàn)initialize方法,而超類實現(xiàn)了,那么就會執(zhí)行超類的這個方法,所以通常會:
5、initialize和load的方法必須寫的精簡。
6、initialize中可以實現(xiàn)無法在編譯期初始化的全局變量,load的方法中可以實現(xiàn)swizzling的邏輯。
7、load的調(diào)用并不視為類的第一個方法完成,因為load中調(diào)用了當(dāng)前類中的方法,就先去執(zhí)行initialize方法了。
8、Runtime調(diào)用+(void)load時沒有autorelease pool
9、load方法調(diào)用的順序:父類(Superclass)的方法優(yōu)先于子類(Subclass)的方法,類中的方法優(yōu)先于類別(Category)中的方法。
10、所有類別(Category)中的load方法都會執(zhí)行。
11、最后一個類別(Category)中的initialize方法會覆蓋之前類別和類中的initialize方法。

2)dispatch_once
由于swizzling會改變?nèi)譅顟B(tài),所以我們使用的時候需要加倍小心,所以在運行時中加上 dispatch_once,
它確保代碼即使在多線程環(huán)境下也只會被執(zhí)行一次。

3) SEL, Method,IMP
SEL:SEL是一個方法在運行時的名字,OC中調(diào)用一個方法,就是向?qū)ο蟀l(fā)送一個消息,通過SEL找到這個方法。

Method:

IMP:IMP就是方法的實現(xiàn)

Method swizzling 就是通過交換方法的實現(xiàn)

//Returns the implementation of a method. A function pointer of type IMP.
method_getImplementation

//Replaces the implementation of a method for a given class.The previous implementation of the method identified by name for the class identified by cls.
class_replaceMethod

//Exchanges the implementations of two methods.
method_exchangeImplementations

4) 在交換的方法里面實現(xiàn)自己的目的

- (void)xxx_viewWillAppear:(BOOL)animated
 {    
    [self xxx_viewWillAppear:animated];     
    NSLog(@"viewWillAppear: %@", NSStringFromClass([self class])); 
}

[self xxx_viewWillAppear:animated]; 在方法里面調(diào)用不會引起循環(huán)調(diào)用,因為這個是運行時已經(jīng)把xxx_viewWillAppear和viewWillAppear調(diào)換,調(diào)用viewWillApper是遵循調(diào)用父類的規(guī)則,避免出現(xiàn)意想不到的問題。

-(BOOL) isKindOfClass: classObj 判斷是否是這個類,包括這個類的子類和父類的實例;
-(BOOL) isMemberOfClass: classObj 判斷是否是這個類的實例,不包括子類或者父類;

參考原文地址:
http://nshipster.com/method-swizzling/
參考文獻(xiàn):
http://justsee.iteye.com/blog/1630979

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 2,051評論 0 9
  • 得承認(rèn),每每看到別人聲稱 method swizzling是一個多么強大的工具,我就很納悶,這工具的應(yīng)用場景有哪些...
    seedante閱讀 635評論 0 2
  • 轉(zhuǎn)載:http://www.cocoachina.com/ios/20161102/17920.html 因為Ob...
    F麥子閱讀 701評論 0 1
  • 一、Method Swizzling 原理 我們知道 OC 是動態(tài)語言,我們執(zhí)行一個函數(shù)的時候,其實是在發(fā)一條消息...
    Cheriez閱讀 452評論 0 0
  • 時光靜寂,歲月輕柔,拈一顆素心,輕倚季節(jié)的轉(zhuǎn)角,看流年的風(fēng)輕輕吹過,始終相信,時光可以帶走最美的年華,歲月可以刻畫...
    紅塵莫笑人閱讀 543評論 0 1

友情鏈接更多精彩內(nèi)容