category的高級使用
1. 分類爲什麼會覆蓋掉類的同名方法,對應的類方法是不存在了麼?
2. 怎麼解除分類對類方法的覆蓋?
3.category怎麼關聯對象的?
4.多個category,哪個方法優先執行?
Category是在
Objective-C 2.0
時提供的新的語言特性,其原因簡單,不管類設計的多麼完美,總有無法預測的狀況,Category就是作爲一種方式來擴展類的。
關於基礎部分,網上很多相關文檔可供查閱,此文不贅述。
作者只做分類的底層及高級用法作使用說明。
1. 分類爲什麼會覆蓋掉類的同名方法,對應的類方法是不存在了麼?
1. category的方法沒有“完全替換掉”原來類已經有的方法,也就是說如果category和原來類都有methodA,那麼category附加完成之後,類的方法列表裏會有兩個methodA
2. category的方法被放到了新方法列表的前面,而原來類的方法被放到了新方法列表的後面,這也就是我們平常所說的category的方法會“覆蓋”掉原來類的同名方法,這是因爲運行時在查找方法的時候是順着方法列表的順序查找的,它只要一找到對應名字的方法,就會罷休^_^,殊不知後面可能還有一樣名字的方法。
2. 怎麼解除分類對類方法的覆蓋?
/*
怎麼調用到原來類中被category覆蓋掉的方法?
對於這個問題,我們已經知道category其實並不是完全替換掉原來類的同名方法,只是category在方法列表的前面而已,所以我們只要順着方法列表找到最後一個對應名字的方法,就可以調用原來類的方法:
*/
+ (void)useClassMethodInsteadCayegoryMethod: (SEL)seletor {
if (self) {
unsigned int methodCount;
Method *methodList = class_copyMethodList([self class], &methodCount);
IMP lastImp = NULL;
SEL lastSel = NULL;
for (NSInteger i = 0; i < methodCount; i++) {
Method method = methodList[i];
NSString *methodName = [NSString stringWithCString:sel_getName(method_getName(method))
encoding:NSUTF8StringEncoding];
NSString *selectorName = NSStringFromSelector(seletor);
if ([selectorName isEqualToString:methodName]) {
lastImp = method_getImplementation(method);
lastSel = method_getName(method);
}
}
typedef void (*fn)(id,SEL);
if (lastImp != NULL) {
fn f = (fn)lastImp;
f(self,lastSel);
}
free(methodList);
}
}
3.category怎麼關聯對象的?
#import <Foundation/Foundation.h>
#import "LearnCategoryClass.h"
@interface LearnCategoryClass (Addition)
@property(nonatomic, strong) NSString *newName;
#import "LearnCategoryClass+Addition.h"
#import <objc/runtime.h>
@implementation LearnCategoryClass (Addition)
- (void)testCategory {
NSLog(@"分類裏是%@,方法名是%s",[self class], __PRETTY_FUNCTION__);
}
//運行時動態添加set和get方法(Xcode9 更新了提醒功能,超級牛逼。比如此處在分類裏聲明瞭屬性,但不添加set/get方法會warnning,這裏說下,厲害了我的蘋果)
- (void)setNewName:(NSString *)newName
{
objc_setAssociatedObject(self,
"newName",
newName,
OBJC_ASSOCIATION_COPY);
}
- (NSString*)newName
{
NSString *nameObject = objc_getAssociatedObject(self, "newName");
return nameObject;
}
@end
4.多個category,哪個方法優先執行?
好吧,這其實不是一個問題,是一串問題。
可以分爲:
- category裏的方法是什麼時候註冊到Class的
method_list
的,在+load
階段麼?
- 如果有多個category,怎麼辦?
我們知道,在類class和category中都可以有+load
方法,那麼有兩個問題:
- 在class的
+load
方法調用的時候,我們可以調用category中聲明的方法麼?
- 這麼多個
+load
方法,調用順序是咋樣的呢?
答案是
- 可以調用,而且附加category到類的工作會先於
+load
方法的執行; -
+load
的執行順序是先class,後category,而category的+load
執行順序是根據編譯順序決定的。
可以這麼理解方法的調用。
有一個類似有壓棧入棧的棧結構,先把class裏的methodA添加到methodList中,然後添加分類中的方法,編譯器會從上到下找分類,先找到的分類就先放入methodList中,後找到的就後放入。
所以最終形成了一個Class裏的方法在最底層,最後編譯的分類在最上層的棧結構。
而方法的調用是從上到下執行的,冉調用對應的方法,就會從methodList裏找對應的SEL,找到就停止,所以Class和前面便輕易的分類雖然都在methodList裏,但因爲找到了就不會繼續查找。
以上就是方法掉用順序的原理。
當然,編譯順序可以在 Build Phases
裏的Compile Source
裏修改。
奇葩問答:
- 如果在分類裏聲明瞭,但不實現,
method_list
裏不會有,當然也不會調用分類方法。但若是實現了,即使不在.h文件聲明,也會調用分類的方法; - 如果分類裏是
- (void)testCategory
,而Class裏是- (int)testCategory
;返回值不同,也會覆蓋,因爲雖然返回值不同,但OC裏,這兩者依然是一個方法。 - 對於多個分類,方法的調用是執行最後編譯的分類方法;
文末
關於category及其OC的深入研究,請轉移到Github—>OCDeepLearning
歡迎star和issues參與討論。
之於category的更底層實現,可以參照:參考文章