category的高級使用
1. 分類為什么會覆蓋掉類的同名方法眨层,對應的類方法是不存在了么?
2. 怎么解除分類對類方法的覆蓋上荡?
3.category怎么關聯(lián)對象的趴樱?
4.多個category,哪個方法優(yōu)先執(zhí)行酪捡?
Category是在
Objective-C 2.0
時提供的新的語言特性伊佃,其原因簡單,不管類設計的多么完美沛善,總有無法預測的狀況航揉,Category就是作為一種方式來擴展類的。
關于基礎部分金刁,網(wǎng)上很多相關文檔可供查閱帅涂,此文不贅述。
作者只做分類的底層及高級用法作使用說明尤蛮。
1. 分類為什么會覆蓋掉類的同名方法媳友,對應的類方法是不存在了么?
1. category的方法沒有“完全替換掉”原來類已經(jīng)有的方法产捞,也就是說如果category和原來類都有methodA醇锚,那么category附加完成之后,類的方法列表里會有兩個methodA
2. category的方法被放到了新方法列表的前面坯临,而原來類的方法被放到了新方法列表的后面焊唬,這也就是我們平常所說的category的方法會“覆蓋”掉原來類的同名方法,這是因為運行時在查找方法的時候是順著方法列表的順序查找的看靠,它只要一找到對應名字的方法赶促,就會罷休^_^,殊不知后面可能還有一樣名字的方法挟炬。
2. 怎么解除分類對類方法的覆蓋鸥滨?
/*
怎么調用到原來類中被category覆蓋掉的方法嗦哆?
對于這個問題,我們已經(jīng)知道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怎么關聯(lián)對象的凸主?
#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__);
}
//運行時動態(tài)添加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但两,哪個方法優(yōu)先執(zhí)行鬓梅?
好吧,這其實不是一個問題谨湘,是一串問題绽快。
可以分為:
- category里的方法是什么時候注冊到Class的
method_list
的,在+load
階段么紧阔?
- 如果有多個category坊罢,怎么辦?
我們知道擅耽,在類class和category中都可以有+load
方法活孩,那么有兩個問題:
- 在class的
+load
方法調用的時候,我們可以調用category中聲明的方法么乖仇?
- 這么多個
+load
方法憾儒,調用順序是咋樣的呢?
答案是
- 可以調用乃沙,而且附加category到類的工作會先于
+load
方法的執(zhí)行起趾; -
+load
的執(zhí)行順序是先class,后category警儒,而category的+load
執(zhí)行順序是根據(jù)編譯順序決定的训裆。
可以這么理解方法的調用。
有一個類似有壓棧入棧的棧結構蜀铲,先把class里的methodA添加到methodList中边琉,然后添加分類中的方法,編譯器會從上到下找分類蝙茶,先找到的分類就先放入methodList中艺骂,后找到的就后放入。
所以最終形成了一個Class里的方法在最底層隆夯,最后編譯的分類在最上層的棧結構钳恕。
而方法的調用是從上到下執(zhí)行的,冉調用對應的方法蹄衷,就會從methodList里找對應的SEL忧额,找到就停止,所以Class和前面便輕易的分類雖然都在methodList里愧口,但因為找到了就不會繼續(xù)查找睦番。
以上就是方法掉用順序的原理。
當然耍属,編譯順序可以在 Build Phases
里的Compile Source
里修改托嚣。
奇葩問答:
- 如果在分類里聲明了,但不實現(xiàn)厚骗,
method_list
里不會有,當然也不會調用分類方法示启。但若是實現(xiàn)了,即使不在.h文件聲明领舰,也會調用分類的方法夫嗓; - 如果分類里是
- (void)testCategory
,而Class里是- (int)testCategory
;返回值不同冲秽,也會覆蓋舍咖,因為雖然返回值不同,但OC里锉桑,這兩者依然是一個方法排霉。 - 對于多個分類,方法的調用是執(zhí)行最后編譯的分類方法民轴;
文末
關于category及其OC的深入研究郑诺,請轉移到Github—>OCDeepLearning
歡迎star和issues參與討論。
之于category的更底層實現(xiàn)杉武,可以參照:參考文章