兩個category方法相同調用哪個
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方法相同調用哪個