前言
對(duì)于iOS開(kāi)發(fā)者而言冲茸,isKindOfClass:
與isMemberOfClass:
應(yīng)該是相當(dāng)熟悉的通孽,今天我們不是要講這兩個(gè)方法的用法仲义,而是討論一個(gè)關(guān)于這兩個(gè)方法的面試題齐蔽。
正文
大家思考一下下面這個(gè)面試題:
BOOL result1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];
BOOL result2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
BOOL result3 = [(id)[PYTeacher class] isKindOfClass:[PYTeacher class]];
BOOL result4 = [(id)[PYTeacher class] isMemberOfClass:[PYTeacher class]];
其實(shí)分析這個(gè)面試題产弹,只要根據(jù) isa
的指向圖派歌,加以分析就能得出正確的結(jié)論:
result1 = YES;
result2 = NO;
result3 = NO;
result4 = NO;
分析過(guò)程:
NSObject類(lèi)對(duì)象
屬于 NSObject元類(lèi)
,NSObject元類(lèi)
的 父類(lèi)
是 NSObject類(lèi)
痰哨,所以result1 = YES
胶果、result2 = NO
。
同理:
PYTeacher類(lèi)對(duì)象
屬于 PYTeacher元類(lèi)
斤斧,但是在 PYTeacher元類(lèi)
的 繼承鏈
中不包含 PYTeacher類(lèi)
稽物,所以result3 = NO
、result4 = NO
折欠。
小結(jié)
本來(lái)分析到這里贝或,這篇文章也就該結(jié)束了。但是锐秦,筆者曾親身經(jīng)歷過(guò)這個(gè)面試題咪奖,也是這樣分析的,但面試官始終追問(wèn)一句:你有沒(méi)有看過(guò) isKindOfClass: 的實(shí)現(xiàn)酱床?
今天羊赵,同樣把這個(gè)問(wèn)題拋給大家:你有沒(méi)有看過(guò) isKindOfClass: 的實(shí)現(xiàn)?
isKindOfClass:
我們可以通過(guò) objc
的源碼查看 isKindOfClass:
的具體實(shí)現(xiàn):
+ (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = self->ISA(); tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
- (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
so easy!
就這么簡(jiǎn)單!一個(gè) for
循環(huán)而已昧捷! ??????
讓我們運(yùn)行一下闲昭,在isKindOfClass:
里打個(gè)斷點(diǎn):
這時(shí),意外發(fā)生了C一印P蚓亍!
程序并沒(méi)有停在斷點(diǎn)這里0掀啤tさ怼框产!
什么情況赚爵?方法為什么沒(méi)有執(zhí)行枉侧???????
這個(gè)方法沒(méi)有執(zhí)行煞赢,肯定是出了什么問(wèn)題隔崎,那能是什么問(wèn)題呢域蜗?我們只是運(yùn)行了一下代碼而已爷抓,難道是編譯器做了手腳卡者?
LLVM
剛才我們想到了編譯器盆赤,那我們就從 LLVM
里面尋找答案珠叔。我們可以在LLVM
的代碼里搜索isKindOfClass
,會(huì)找到如下內(nèi)容:
// This is the table of ObjC "accelerated dispatch" functions. They are a set
// of objc methods that are "seldom overridden" and so the compiler replaces the
// objc_msgSend with a call to one of the dispatch functions. That will check
// whether the method has been overridden, and directly call the Foundation
// implementation if not.
// This table is supposed to be complete. If ones get added in the future, we
// will have to add them to the table.
const char *AppleObjCTrampolineHandler::g_opt_dispatch_names[] = {
"objc_alloc",
"objc_autorelease",
"objc_release",
"objc_retain",
"objc_alloc_init",
"objc_allocWithZone",
"objc_opt_class",
"objc_opt_isKindOfClass",
"objc_opt_new",
"objc_opt_respondsToSelector",
"objc_opt_self",
};
注釋的大體意思就是弟劲,這是個(gè)加速調(diào)度的函數(shù)表祷安,這里面是一些很少被覆蓋的objc
的方法,所以編譯器會(huì)用他們替換objc_msgSend
兔乞。我們發(fā)現(xiàn)其中有一個(gè)objc_opt_isKindOfClass
汇鞭,我們猜測(cè)編譯器會(huì)用他來(lái)替換isKindOfClass:
方法。我們?cè)?objc
的源碼中搜索這個(gè)方法庸追,并打一個(gè)斷點(diǎn)霍骄。
如圖2中,果然像我們猜測(cè)的那樣淡溯,調(diào)用了
objc_opt_isKindOfClass
方法读整,而這個(gè)方法的主要內(nèi)容也是一個(gè) for
循環(huán),至此 isKindOfClass:
我們就分析完了咱娶。??
isMemberOfClass
isMemberOfClass
的源碼如下:
+ (BOOL)isMemberOfClass:(Class)cls {
return self->ISA() == cls;
}
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
isMemberOfClass
的源碼并不復(fù)雜米间,這里就不再贅述了。?????
總結(jié)
當(dāng)我們?cè)谡{(diào)用isKindOfClass:
方法的時(shí)候膘侮,編譯器已經(jīng)把方法替換了屈糊,實(shí)際運(yùn)行的時(shí)候,會(huì)調(diào)用 objc_opt_isKindOfClass
方法琼了,方法內(nèi)部會(huì)通過(guò)一個(gè) for
循環(huán)來(lái)追溯逻锐,對(duì)象所屬的類(lèi)在不在目標(biāo)類(lèi)的繼承鏈中。