
本系列博客是本人的源碼閱讀筆記,如果有 iOS 開發(fā)者在看 runtime 的,歡迎大家多多交流。為了方便討論,本人新建了一個微信群(iOS技術(shù)討論群),想要加入的,請?zhí)砑颖救宋⑿牛簔hujinhui207407,【加我前請備注:ios 】,本人博客http://www.kyson.cn 也在不停的更新中,歡迎一起討論
本文完整版詳見筆者小專欄:https://xiaozhuanlan.com/runtime
前言
NSObject對象是iOS開發(fā)者都很熟悉的對象,它幾乎是所有對象的根類。在任何.m文件中輸入以下代碼:
NSObject
點(diǎn)擊NSObject跳轉(zhuǎn)到其定義文件,發(fā)現(xiàn)如下聲明:
@interface NSObject <NSObject> {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-interface-ivars"
Class isa OBJC_ISA_AVAILABILITY;
#pragma clang diagnostic pop
}
其中#pragma clang diagnostic push用于去除警告,因此,我們能發(fā)現(xiàn)NSObject對象只有一個Class類型的成員變量:isa。
那么:
- 什么是
isa - 什么是
Class類型,與class方法有何區(qū)別
這篇文章將要給大家揭曉該問題。
我們知道任何一個類都有 class方法比如:
[NSObject class];
當(dāng)然還有superclass方法:
[NSObject superclass];
更多和clas相關(guān)的方法列舉如下:
- (BOOL)isKindOfClass:(Class)aClass;
- (BOOL)isMemberOfClass:(Class)aClass;
這兩個方法相信大家都不陌生,只不過這兩個方法位于@property NSObject中,但NSObject中還是有其實(shí)現(xiàn)的。所以我們有理由相信,NSObject中的成員變量isa是有特殊含義的,點(diǎn)擊改成員變量的類型Class我們可以看到其定義:
typedef struct objc_class *Class;
繼續(xù)點(diǎn)擊objc_class:
struct objc_class : objc_object {
//這里省略成員變量以及方法...
}
再次點(diǎn)擊objc_object:
struct objc_object {
private:
isa_t isa;
//這里省略成員變量以及方法...
}
層次有點(diǎn)深,但大家只關(guān)注其結(jié)構(gòu)即可:
Class本質(zhì)是一個結(jié)構(gòu)體。
關(guān)于結(jié)構(gòu)體,大家應(yīng)該都有所了解,這里再做個復(fù)習(xí)吧:
C語言和C++都支持結(jié)構(gòu)體,只是C++的結(jié)構(gòu)體基本上和類沒有區(qū)別。以下是摘自知乎某答主:
結(jié)構(gòu)體和類的區(qū)別
本質(zhì)上來說結(jié)構(gòu)體與類是同一個東西,可是默認(rèn)情況下基于可讀性的原因還是加一些區(qū)分:
結(jié)構(gòu)體就只含數(shù)據(jù)成員和構(gòu)造函數(shù)、析構(gòu)函數(shù),盡可能保持簡單。
類則包含更多的非構(gòu)造、析構(gòu)成員函數(shù),概念更大,用來描述普遍意義上的對象類型。
我們有理由相信:NSObject對象的各個方法,基本上是針對其結(jié)構(gòu)體isa對象的操作。這里我們研究幾個我們常用的方法:
本文完整版詳見筆者小專欄:https://xiaozhuanlan.com/runtime
isMemberOfClass:
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
很簡單,判斷一下,當(dāng)前的class方法是否等于參數(shù)。
因?yàn)?code>self是NSObject對象,因此我們查看class方法:
- (Class)class {
return object_getClass(self);
}
點(diǎn)擊object_getClass查看其定義:
Class object_getClass(id obj)
{
if (obj) return obj->getIsa();
else return Nil;
}
繼續(xù)進(jìn)入方法getIsa:
inline Class
objc_object::getIsa()
{
if (!isTaggedPointer()) return ISA();
uintptr_t ptr = (uintptr_t)this;
if (isExtTaggedPointer()) {
uintptr_t slot =
(ptr >> _OBJC_TAG_EXT_SLOT_SHIFT) & _OBJC_TAG_EXT_SLOT_MASK;
return objc_tag_ext_classes[slot];
} else {
uintptr_t slot =
(ptr >> _OBJC_TAG_SLOT_SHIFT) & _OBJC_TAG_SLOT_MASK;
return objc_tag_classes[slot];
}
}
可以發(fā)現(xiàn),越牽扯越深,閱讀有點(diǎn)困難了。但大家別著急,我們可以屏蔽
if (!isTaggedPointer()) return ISA();
以下的代碼,關(guān)于什么是TaggedPointer,筆者會在后面的文章中分析給大家。因此上面的代碼可以先簡化成:
inline Class
objc_object::getIsa()
{
if (!isTaggedPointer()) return ISA();
}
繼續(xù)研究ISA()方法:
inline Class
objc_object::ISA()
{
assert(!isTaggedPointer());
#if SUPPORT_INDEXED_ISA
if (isa.nonpointer) {
uintptr_t slot = isa.indexcls;
return classForIndex((unsigned)slot);
}
return (Class)isa.bits;
#else
return (Class)(isa.bits & ISA_MASK);
#endif
}
同樣去掉暫時不需要我們理解的部分,簡化代碼如下:
inline Class
objc_object::ISA()
{
return (Class)(isa.bits & ISA_MASK);
}
至此,我們可以看到class方法最終獲取的即是:結(jié)構(gòu)體objc_object的isa.bits & ISA_MASK的結(jié)果。
那,大家的疑問也會隨之而來:
-
inline關(guān)鍵字作用,為何這里的幾個方法實(shí)現(xiàn)都在.h文件中 - 在方法:
objc_object::ISA()中雙冒號的作用。 - objc_object 中的isa又是什么
- isa.bits & ISA_MASK 的含義
inline關(guān)鍵字
用來定義一個類的內(nèi)聯(lián)函數(shù),引入它的主要原因是用它替代C中表達(dá)式形式的宏定義。
也就是說,用inline關(guān)鍵字修飾的是內(nèi)聯(lián)函數(shù),內(nèi)聯(lián)函數(shù)用于替代宏定義。取代宏定義的原因是:
- C中使用define這種形式宏定義的原因是因?yàn)椋珻語言是一個效率很高的語言,這種宏定義在形式及使用上像一個函數(shù),但它使用預(yù)處理器實(shí)現(xiàn),沒有了參數(shù)壓棧,代碼生成等一系列的操作,因此,效率很高,這是它在C中被使用的一個主要原因。
- 這種宏定義在形式上類似于一個函數(shù),但在使用它時,僅僅只是做預(yù)處理器符號表中的簡單替換,因此它不能進(jìn)行參數(shù)有效性的檢測,也就不能享受C++編譯器嚴(yán)格類型檢查的好處,另外它的返回值也不能被強(qiáng)制轉(zhuǎn)換為可轉(zhuǎn)換的合適的類型,這樣,它的使用就存在著一系列的隱患和局限性。
- 在C++中引入了類及類的訪問控制,這樣,如果一個操作或者說一個表達(dá)式涉及到類的保護(hù)成員或私有成員,你就不可能使用這種宏定義來實(shí)現(xiàn)(因?yàn)闊o法將this指針放在合適的位置)。
- inline 推出的目的,也正是為了取代這種表達(dá)式形式的宏定義,它消除了宏定義的缺點(diǎn),同時又很好地繼承了宏定義的優(yōu)點(diǎn)。
雙冒號
用于表示“域操作符”,例:聲明了一個類A,類A里聲明了一個成員函數(shù)void f(),但沒有在類的聲明里給出f的定義,那么在類外定義f時,就要寫成void A::f(),表示這個f()函數(shù)是類A的成員函數(shù)。
objc_object 中的isa
之前我們已經(jīng)寫了objc_object的定義,可以知道,isa其實(shí)是一個isa_t的對象,那isa_t是什么呢,我們繼續(xù)看一下它的實(shí)現(xiàn):
union isa_t
{
//這里省略很多變量
}
可以知道,isa_t是個聯(lián)合體,也就是說:objc_object 中的isa其實(shí)是個結(jié)構(gòu)體
isa.bits & ISA_MASK 含義
上面我們知道,isa是個聯(lián)合體,其內(nèi)部的屬性bits呢?
union isa_t
{
//省略部分方法和屬性...
uintptr_t bits;
然后看uintptr_t實(shí)現(xiàn):
typedef unsigned long uintptr_t;
發(fā)現(xiàn)其是個unsigned long類型,而ISA_MASK的定義如下:
# if __arm64__
# define ISA_MASK 0x0000000ffffffff8ULL
# elif __x86_64__
# define ISA_MASK 0x00007ffffffffff8ULL
# else
# error unknown architecture for packed isa
# endif
可知,其實(shí)ISA_MASK還是個數(shù)值類型。也就是說判斷兩個對象是否是同一個class其實(shí)是通過比對objc_object中的數(shù)值計算后得出的結(jié)果是否相等得出的。
講完了 isMemberOfClass方法,isKindOfClass方法這里就不多做介紹了,給出其源代碼即可:
isKindOfClass:
其實(shí)現(xiàn)如下:
+ (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = object_getClass((id)self); tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
總結(jié)
-
Class類型本質(zhì)是個結(jié)構(gòu)體,該結(jié)構(gòu)體中存儲了該NSObject中的所有信息。 - 比對兩個類是否是同一個類,其實(shí)是判斷
Class中的某個數(shù)值運(yùn)算的結(jié)果是否相等。
本文完整版詳見筆者小專欄:https://xiaozhuanlan.com/runtime
廣告
我的首款個人開發(fā)的APP壁紙寶貝上線了,歡迎大家下載。
