1. 程式人生 > >兩個category方法相同調用哪個

兩個category方法相同調用哪個

似的 ~~ data- category 擴展 自動 很難 imageview 成員

Category擴展,它是對一個類進行功能的擴展。
在項目的開發過程中,在不斷的叠代開發過程中,我們的類也不可避免的要根據需求來增加新的功能,而這個時候很多的人可能會新建一個子類,然後在子類中去增加我們的新功能,這確實能夠實現我們的目的,但是久而久之,我們會因為新建的類越來越多,導致項目也越來越龐大,而且也很難管理,這個時候Category就派上用場了,我們可以將一組具有相似的功能的擴展放在一個Category裏面,這樣就可以進行模塊化劃分功能。

Category的調用

首先我們來了解一下類擴展,涉及到多個擴展重寫類的同一個方法的調用順序

1、一個類的擴展的情況

首先創建一個Person類,裏面有一個addPerson:方法,然後建立一個Person類的擴展Person+Extend,裏面可以擴展你的其他方法,在這裏我們實驗一下繼續擴展addPerson:方法,看看調用的順序。

Person類

@implementation Person

- (void)addPerson:(NSString *)a {

    NSLog(@"Person類裏面~~~~~~%@", self);
}

@end

擴展Extend裏面

@implementation Person (Extend)

- (void)addPerson:(NSString *)a {

    NSLog(@"Extend~~~~~~%@", self);
}

@end

調用

 - (void)test3 {

    Person *person = [[Person alloc] init];

    [person addPerson:@"a"];

}

發現只打印了擴展的輸出:

2017-03-24 22:37:49.135 RunTimeDemo[84566:15956638] Extend~~~~~~<Person: 0x608000004cd0>

並沒有打印Person原來方法的addPerson:方法,所以擴展有更高的優先級,如果擴展裏出現了和原類裏面相同的方法,那麽會直接調用擴展裏的方法,而不會調用類裏面原來的方法。這裏擴展的方法不是覆蓋了類裏面的方法,類裏面的方法和擴展裏的方法都存在類的結構空間,但是最先尋找到的是擴展裏的方法,所以先調用該方法的實現。更具體的原因會在之後用runtime進行解釋。

2、兩個類擴展的情況

在上文的基礎上,我們在創建一個Person的擴展Person+A,然後同樣實現了addPerson:的方法,然後在調用這個方法,看一下編譯器會調用哪個方法,還是兩個方法都會調用。

@implementation Person (A)

- (void)addPerson:(NSString *)a {

    NSLog(@"A~~~~~~%@", self);
}

@end

調用

- (void)test3 {

    Person *person = [[Person alloc] init];

    [person addPerson:@"a"];

}

發現文件打印的是:

2017-03-24 22:45:39.342 RunTimeDemo[84658:16064399] A~~~~~~<Person: 0x600000009be0>

那麽原來Extend擴展裏的addPerson:方法呢,為什麽沒有調用呢?
上面的編譯的順序是

技術分享
調用順序

首先我們修改一下編譯的順序,將Person+A和Person+Extend的編譯順序換一下

技術分享
調用順序

我們看到打印出來的結果又變成了

2017-03-24 22:51:58.767 RunTimeDemo[84770:16152019] Extend~~~~~~<Person: 0x6000000187d0>

這個實驗說明當有兩個擴展裏面有同一個方法時,會調用後編譯的那個擴展裏的方法,為什麽會這樣呢,原因是當類有擴展方法的時候,擴展方法的鏈表就放在原來類的方法鏈表的前面,那麽在尋找方法的時候,會從前面開始尋找方法,當尋找到第一個這個方法的時候,返回方法的實現調用,所以根據上面兩個編譯順序,我用下面的圖來解釋一下:

技術分享
擴展方法結構圖

所以當類有多個擴展的時候,擴展裏有相同的方法的時候,會調用最後面編譯的那個擴展裏的方法。

3、在類擴展裏進行方法交換

現在暫時的屏蔽掉Person+Extend的方法,而新加一個Person+B擴展,現在對Person+A和Person+B進行方法交換。

Person+A

@implementation Person (A)

+ (void)load
{
    Method originalMethod = class_getInstanceMethod(self, @selector(addPerson:));
    Method swizzledMethod = class_getInstanceMethod(self, @selector(a_addPerson:));
    method_exchangeImplementations(originalMethod, swizzledMethod);
}

- (void)a_addPerson:(NSString *)a {

    NSLog(@"A類裏面~~~~~~%@", self);

    [self a_addPerson:a];

}

@end

Person+B

@implementation Person (B)

+ (void)load
{
    Method originalMethod = class_getInstanceMethod(self, @selector(addPerson:));
    Method swizzledMethod = class_getInstanceMethod(self, @selector(b_addPerson:));
    method_exchangeImplementations(originalMethod, swizzledMethod);
}

- (void)b_addPerson:(NSString *)a {

    NSLog(@"B類裏面~~~~~~%@", self);

    [self b_addPerson:a];

}

@end

調用

- (void)test3 {

    Person *person = [[Person alloc] init];

    [person addPerson:@"a"];

}

打印出來的結果

技術分享
結果

發現A擴展和B擴展的方法都調用了,這裏擴展的不是addPerson:方法,其實是+load方法,+load方法是一個類所在的文件引用就會調用,而+load方法是所有的擴展文件都會調用的,所以在這裏看看編譯的順序

技術分享
編譯順序

所以這裏看當Person+B擴展裏的+load方法被調用的時候,將Person類裏面本來的方法的實現和B擴展裏的方法的實現進行了交換,然後編譯到Person+A方法的時候,將Person的方法和A的方法交換,此時Person裏面方法的實現是B,所以就相當於將B的實現和A的實現進行了交換,所以就有了最後的一個圖,所以三個方法都交換了方法的實現,當調用Person的方法的時候,實際上是調用A的方法,然後調用A方法的時候調用的是B方法,最後再是真正意義上的Person的方法。

技術分享
交換方法

當改變編譯的順序的時候,可以看到打印順序也換了

技術分享
改變編譯順序

打印出結果

技術分享
結果

4、對類擴展進行方法交換和單獨的類擴展

現在將Person+Extend文件打開,同時Person+A和Person+B進行方法交換

打印出來的結果

技術分享
結果

此時沒有打印Person裏面的方法,說明方法交換的時候交換的是Extend擴展裏的方法。

5、對類擴展添加一個屬性的時候

在 Person+Extend.h 的擴展裏加一個屬性,然而 Person+Extend.m裏並沒有實現setter和getter方法

Person+Extend

@interface Person (Extend)

@property (nonatomic, copy) NSString *name;

@end

調用

- (void)test3 {

    Person *person = [[Person alloc] init];

    person.name = @"ffff";

}

會直接報錯

技術分享
錯誤

當我們在Person的init方法裏打印self.name的時候,會發現並沒有這個name屬性

技術分享
init

說明在類的擴展中添加屬性的時候,編譯器並沒有自動為我們添加setter和getter的方法,而擴展裏添加屬性也並不是添加了成員變量,而我們訪問這個屬性的時候,其實是訪問setter和getter方法。

測試代碼
https://github.com/xlym33/CategoryDemo



轉自:http://www.jianshu.com/p/8719e1544860

兩個category方法相同調用哪個