對(duì)OC中Category 的理解,以及方法覆蓋的解決辦法

在OC 中,使用category會(huì)讓我們?cè)陂_發(fā)中非常方便,可以為某個(gè)類增添方法,對(duì)類別自己有一點(diǎn)小小的體會(huì),首先先來(lái)介紹一下類別

類別是在運(yùn)行時(shí)決定的,在運(yùn)運(yùn)行時(shí)才會(huì)分配相應(yīng)的內(nèi)存空間,在就決定了在編譯期是不能給某個(gè)類增加屬性,否則會(huì)影響內(nèi)存布局,從而導(dǎo)致crash ,若想給某個(gè)類增加一個(gè)屬性,需要在運(yùn)行時(shí)添加,首先先介紹下類別的結(jié)構(gòu)

typedef struct category_t {
? ?const char *name;
classref_t cls;
struct method_list_t *instanceMethods;
struct method_list_t *classMethods;
struct protocol_list_t *protocols;
struct property_list_t *instanceProperties;
} category_t;

這個(gè)結(jié)構(gòu)體理念包含這個(gè)類別的名字,實(shí)例方法,類方法,協(xié)議名,和實(shí)例屬性

正如我們所知的那樣,當(dāng)我們重寫類的一個(gè)方法以后,會(huì)覆蓋原來(lái)類的方法,它為什么會(huì)覆蓋原來(lái)類的方法呢,我們先來(lái)看一下當(dāng)調(diào)用一個(gè)方法的時(shí)候是怎么運(yùn)行的

BOOL classExists = NO;
? ? ? ? ? ?if (cat->instanceMethods || ?cat->protocols
|| ?cat->instanceProperties)
{
addUnattachedCategoryForClass(cat, cls, hi);
if (isRealized(cls)) {
remethodizeClass(cls);
classExists = YES;
}
if (PrintConnecting) {
_objc_inform("CLASS: found category -%s(%s) %s",
getName(cls), cat->name,
classExists ? "on existing class" : "");
}
}
if (cat->classMethods ?|| ?cat->protocols
/* || ?cat->classProperties */)
{
addUnattachedCategoryForClass(cat, cls->isa, hi);//關(guān)聯(lián)映射
if (isRealized(cls->isa)) {
remethodizeClass(cls->isa);//添加事件
}
if (PrintConnecting) {
_objc_inform("CLASS: found category +%s(%s)",
getName(cls), cat->name);
}
} ?

在這里面我們可以看到,是先根據(jù)isa 指針找到該類,然后在該類的方法列表里面找到該方法的實(shí)現(xiàn),當(dāng)重寫一個(gè)類的方法后,它會(huì)將重寫的方法,放在方法列表的第一位,當(dāng)?shù)谝淮握业皆摲椒ǖ臅r(shí)候就不會(huì)在查找,就會(huì)執(zhí)行重寫的方法實(shí)現(xiàn),如果我們想執(zhí)行原來(lái)的方法實(shí)現(xiàn)就可以跳過(guò)方法列表前面的方法實(shí)現(xiàn),執(zhí)行最后一個(gè)該方法的實(shí)現(xiàn)就可以了

Class currentClass = [MyClass class];
MyClass *my = [[MyClass alloc] init];

if (currentClass) {
unsigned int methodCount;
Method *methodList = class_copyMethodList(currentClass, &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];
if ([@"printName" isEqualToString:methodName]) {
lastImp = method_getImplementation(method);
lastSel = method_getName(method);
}
}
typedef void (*fn)(id,SEL);

if (lastImp != NULL) {
fn f = (fn)lastImp;
f(my,lastSel);
}
free(methodList);
}

上面的printName為重寫的方法名,獲取最后一個(gè)類名和方法實(shí)現(xiàn),這樣就可以調(diào)用被重寫的方法實(shí)現(xiàn)。

前面提到不可以在編譯期給類增加屬性,那可以在運(yùn)行期動(dòng)態(tài)的給某個(gè)類添加屬性,可以調(diào)用message API來(lái)實(shí)現(xiàn)

+ (void)load
{
NSLog(@"%@",@"load in Category1");
}

- (void)setName:(NSString *)name
{
objc_setAssociatedObject(self,
"name",
name,
OBJC_ASSOCIATION_COPY);
}

- (NSString*)name
{
NSString *nameObject = objc_getAssociatedObject(self, "name");
return nameObject;
}

我們對(duì)某個(gè)類得分類中增加一個(gè)name 屬性,重寫name 的set 和get方法就可以為其添加屬性了,此外給大家奉上我做的一個(gè)相關(guān)的demo 地址類別的方法覆蓋和添加屬性此外在此謝謝美團(tuán)技術(shù)團(tuán)隊(duì)的技術(shù)分享美團(tuán)類別分享

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

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

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