前言
iOS底層探索之對(duì)象原理(二)我們了解到 isa是一個(gè)聯(lián)合體位域,ISA_BITFIELD存儲(chǔ)了類(lèi)的一些信息,本文我們將繼續(xù)探索isa是如何關(guān)聯(lián)對(duì)象與類(lèi)的呢?以及isa的走位分析
alloc流程
iOS底層探索之對(duì)象原理(一)我們探索了《alloc底層原理》知道給一個(gè)對(duì)象發(fā)送alloc消息,在底層源碼會(huì)來(lái)到_objc_rootAlloc,然后進(jìn)入callAlloc。但其實(shí)這里漏掉了一個(gè)過(guò)程,就是objc_alloc
dyld在加載Mach-O二進(jìn)制文件的時(shí)候,會(huì)進(jìn)行符號(hào)綁定的操作,也就是說(shuō)sel_alloc綁定到了 objc_alloc上面去了。 這一步其實(shí)沒(méi)有真正開(kāi)源。
不過(guò)我們?cè)?libobjc 源碼中全局搜索 objc_alloc 即可發(fā)現(xiàn)如下代碼:
static void
fixupMessageRef(message_ref_t *msg)
{
msg->sel = sel_registerName((const char *)msg->sel);
if (msg->imp == &objc_msgSend_fixup) {
if (msg->sel == SEL_alloc) {
msg->imp = (IMP)&objc_alloc;
}
/* 省略下面的代碼 */
}
}
而 fixupMessageRef 的調(diào)用則是在 _read_images 也就是 dyld 讀取我們的鏡像文件的時(shí)候,什么意思呢,這里我們?cè)倏?_read_images 的源碼:
#if SUPPORT_FIXUP
// Fix up old objc_msgSend_fixup call sites
for (EACH_HEADER) {
message_ref_t *refs = _getObjc2MessageRefs(hi, &count);
if (count == 0) continue;
if (PrintVtables) {
_objc_inform("VTABLES: repairing %zu unsupported vtable dispatch "
"call sites in %s", count, hi->fname());
}
for (i = 0; i < count; i++) {
fixupMessageRef(refs+i);
}
}
ts.log("IMAGE TIMES: fix up objc_msgSend_fixup");
#endif
從代碼不難看出,我們?cè)谧x取鏡像文件的時(shí)候,判斷如果需要 fixup ,如果需要的話(huà),我們就調(diào)用 fixupMessageRef ,然后在 fixupMessageRef 內(nèi)部,我們判斷當(dāng)前消息的 SEL 是否是 SEL_alloc,如果是的話(huà)就替換其 IMP 為 objc_alloc 。這一流程只會(huì)走一次,也就是說(shuō) objc_alloc 只會(huì)走一次。

isa的走位分析
我們都知道對(duì)象可以創(chuàng)建多個(gè),但是類(lèi)是否可以創(chuàng)建多個(gè)呢?
答案很簡(jiǎn)單,一個(gè)。那么如果來(lái)驗(yàn)證呢?

接著我們開(kāi)始如下驗(yàn)證:
//MARK: - 分析類(lèi)對(duì)象內(nèi)存存在個(gè)數(shù)
void lgTestClassNum(){
Class class1 = [LGPerson class];
Class class2 = [LGPerson alloc].class;
Class class3 = object_getClass([LGPerson alloc]);
Class class4 = [LGPerson alloc].class;
NSLog(@"\n%p-\n%p-\n%p-\n%p",class1,class2,class3,class4);
}
// 打印輸出如下:
0x100002108-
0x100002108-
0x100002108-
0x100002108
所以我們就知道了類(lèi)在內(nèi)存中只會(huì)存在一份。
(lldb) x/4gx LGTeacher.class
0x100001420: 0x001d8001000013f9 0x0000000100b38140
0x100001430: 0x00000001003db270 0x0000000000000000
(lldb) po 0x001d8001000013f9
17082823967917874
(lldb) p 0x001d8001000013f9
(long) $2 = 8303516107936761
(lldb) po 0x100001420
LGTeacher
我們通過(guò)上面的打印,就發(fā)現(xiàn) 類(lèi)的內(nèi)存結(jié)構(gòu)里面的第一個(gè)結(jié)構(gòu)打印出來(lái)還是 LGTeacher,那么是不是就意味著 對(duì)象->類(lèi)->類(lèi) 這樣的死循環(huán)呢?這里的第二個(gè)類(lèi)其實(shí)是 元類(lèi)。是由系統(tǒng)幫我們創(chuàng)建的。這個(gè)元類(lèi)也無(wú)法被我們實(shí)例化。
也就是下面的這種關(guān)系: 對(duì)象 ——> 類(lèi)對(duì)象 ——> 元類(lèi)
那么好奇心來(lái)了,元類(lèi)的 isa 又是什么呢,在Xcode測(cè)試有以下結(jié)果:

isa走位 & 繼承關(guān)系結(jié)論
- isa:對(duì)象 ——> 類(lèi)對(duì)象 ——> 元類(lèi) ——> 根元類(lèi) ——> 根元類(lèi)自己
- superclass:NSObject 父類(lèi)是nil 、根元類(lèi)的父類(lèi)是 NSObject
對(duì)象的本質(zhì)
在我們認(rèn)知里面,OC 對(duì)象的本質(zhì)就是一個(gè)結(jié)構(gòu)體,這個(gè)結(jié)論在 libobjc 源碼的 objc-private.h 源文件中可以得到證實(shí)。
struct objc_object {
private:
isa_t isa;
public:
// ISA() assumes this is NOT a tagged pointer object
Class ISA();
// getIsa() allows this to be a tagged pointer object
Class getIsa();
/* 省略其他的內(nèi)容 */
}
而對(duì)于對(duì)象所屬的類(lèi)來(lái)說(shuō),我們也可以在 objc-runtime-new.h 源文件中找到
struct objc_class : objc_object {
// Class ISA;
Class superclass;
cache_t cache; // formerly cache pointer and vtable
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
/* 省略其他的內(nèi)容 */
}
也就是說(shuō) objc_class 內(nèi)存中第一個(gè)位置是 isa,第二個(gè)位置是 superclass,我們可以通過(guò) LLDB 打印內(nèi)存地址來(lái)驗(yàn)證:
很簡(jiǎn)單,我們只需要使用 clang 的一個(gè)命令來(lái)編譯我們的 OC 源文件即可。
// 編譯底層源碼
clang -rewrite-objc main.m -o main.cpp
// 存在UIkit系統(tǒng)其他動(dòng)態(tài)庫(kù)引用問(wèn)題 則使用:
clang -x objective-c -rewrite-objc -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk ViewController.m
// xcrun xcode 命令
xcrun -sdk iphonesimulator clang -rewrite-objc ViewController.m
xcrun -sdk iphoneos clang -rewrite-objc ViewController.m
clang -rewrite-objc main.m -o main.cpp 這行命令會(huì)把我們的 main.m 文件編譯成 C++ 格式,輸出為 main.cpp。

通過(guò)觀察,我們可以看到有一個(gè)
NSObject_IVARS 這個(gè)其實(shí)是 NSObject 里面的成員變量。
- 成員變量 (不會(huì)生成 getter 和 setter)
- 實(shí)例變量是一種特殊的成員變量,是由類(lèi)聲明而來(lái)
- 屬性 (LLVM 會(huì)幫我們自動(dòng)生成 getter 和 setter )
附上isa初始化圖
