四、isa 指針關(guān)聯(lián)類

主要內(nèi)容:
1.OC對象的本質(zhì)
2.isa 與 類的關(guān)聯(lián)原理
3.isa 與 類的關(guān)聯(lián)驗(yàn)證

1.OC對象的本質(zhì)

先了解編譯器:clang

\color{#F00000}{Clang} 是一個由Apple主導(dǎo)編寫,基于\color{#F00000}{ LLVM的C/C++/Objective-C編譯器}
\color{#F00000}{Clang} 主要用于通過\color{#F00000}{底層編譯 } 把 .m 文件 編譯輸出成 .cpp, 可以看到\color{#F00000}{底層結(jié)構(gòu)}\color{#F00000}{底層實(shí)現(xiàn)邏輯},方便更好的理解底層。

常用命令:

// 把目標(biāo)文件編譯成c++文件
clang -rewrite-objc main.m -o main.cpp

// 編譯 引用了類似UIKit的文件
clang -rewrite-objc -fobjc-arc -fobjc-runtime=ios-13.0.0 -isysroot / Applications/Xcode.app/Contents/Developer/Platforms/ iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator13.0.sdk viewController.m

//xcode安裝的時候順帶安裝了xcrun命令,xcrun命令在clang的基礎(chǔ)上進(jìn)行了 一些封裝,要更好用一些
xcrun -sdk iphonesimulator clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp (模擬器)
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main- arm64.cpp (手機(jī))

探索對象本質(zhì)

  • 首先自定義 LGPerson 類
@interface LGPerson : NSObject
@property (nonatomic, copy) NSString *name;
@end

@implementation LGPerson
@end
  • 通過終端編譯命令,生成main.cpp文件
clang -rewrite-objc main.m -o main.cpp
  • 打開編譯好的 main.cpp,發(fā)現(xiàn)對象在底層會被編譯成結(jié)構(gòu)體
LGPerson.png

問題:第一行是什么?
第一個屬性:NSObject_IVARS 是 \color{#F00000}{ isa} 因?yàn)樵赾里可以\color{#F00000}{偽繼承} ,偽繼承是直接把GLPerson中的\color{#F00000}{第一個屬性}定義為 NSObject結(jié)構(gòu)體,意味著 \color{#F00000}{LGPerson }擁有 \color{#F00000}{NSObject中的所有成員變量}。

總結(jié)
a.\color{#F00000}{OC對象的本質(zhì)}\color{#F00000}{結(jié)構(gòu)體}
b.\color{#F00000}{ LGPerson} 會繼承 \color{#F00000}{NSObject 的成員變量}

擴(kuò)展:

繼續(xù)向下看 發(fā)現(xiàn)有屬性name 對應(yīng)的 get 和 set 方法 ,如下圖,其中 set 方法調(diào)用了 objc_setProperty

屬性的get和set.png
  • 先看 objc_setProperty 的底層源碼實(shí)現(xiàn)


    objc_setProperty.png
  • 進(jìn)入 reallySetProperty 的源碼,主要是\color{#F00000}{ retain 新值,release 舊值 }

    reallySetProperty.png

在當(dāng)前的set 方法里會調(diào)取 objc_setProperty

objc_setProperty.png

總結(jié):

  • 每一個\color{#F00000}{set方法} 都在做同一件事情:\color{#F00000}{retain 新值,release 舊值},如果每個都對接,就會生成很多方法,很難去找去處理,所以每個set方法都會找到一個函數(shù) \color{#F00000}{setPeoperty};
  • 那么屬性是怎么區(qū)分的?根據(jù)\color{#F00000}{cmd}走,這里cmd 就是 setName 做到\color{#F00000}{接口隔離} (這是一種設(shè)計(jì)思路)。

2.isa 與 類的關(guān)聯(lián)原理

OC的對象就是一個結(jié)構(gòu)體本質(zhì),會繼承自NSObject中的isa, alloc&init探索 中也注意到了 \color{#F00000}{initInstanceIsa},接下來探索 如何將 cls 與 isa 關(guān)聯(lián)的

在此之前,先了解 什么是 \color{#F00000}{聯(lián)合體位域}

聯(lián)合體與結(jié)構(gòu)體的區(qū)別

\color{#F00000}{結(jié)構(gòu)體(struct)}中所有變量是\color{#F00000}{“共存”}
優(yōu)點(diǎn):是“有容乃大”, 全面;
缺點(diǎn):是struct內(nèi)存空間的分配是粗放的,不管用不用,全分配。

\color{#F00000}{聯(lián)合體(union)}中是各變量是\color{#F00000}{“互斥”}
缺點(diǎn):就是不夠“包容”;
優(yōu)點(diǎn):是內(nèi)存使用更為精細(xì)靈活,也節(jié)省了內(nèi)存空間

isa的類型isa_t

isa_t的定義:ISA 是\color{#F00000}{8字節(jié)} 也就是64位 通過\color{#F00000}{聯(lián)合體}定義 能夠存儲很多信息,這樣可以節(jié)省內(nèi)存,提高性能,其中 Class cls 與 bits 是互斥的

isa_t的結(jié)構(gòu)體.png
ISA_BITFIELD.png

\color{#F00000}{nonpointer}:表示是否對 isa 指針開啟指針優(yōu)化 0:純isa指針,1:不止是類對象地址,isa 中包含了類信息、對象的引用計(jì)數(shù)等

\color{#F00000}{has_assoc}:關(guān)聯(lián)對象標(biāo)志位,0沒有,1存在

\color{#F00000}{has_cxx_dtor}:該對象是否有 C++ 或者 Objc 的析構(gòu)器,如果有析構(gòu)函數(shù),則需要做析構(gòu)邏輯, 如果沒有,則可以更快的釋放對象

\color{#F00000}{shiftcls}:存儲類指針的值。開啟指針優(yōu)化的情況下,在 arm64 架構(gòu)中有 33 位用來存儲類指針。

\color{#F00000}{magic}:用于調(diào)試器判斷當(dāng)前對象是真的對象還是沒有初始化的空間

\color{#F00000}{weakly_referenced}:志對象是否被指向或者曾經(jīng)指向一個 ARC 的弱變量,沒有弱引用的對象可以更快釋放。 deallocating:標(biāo)志對象是否正在釋放內(nèi)存

\color{#F00000}{has_sidetable_rc}:當(dāng)對象引用技術(shù)大于 10 時,則需要借用該變量存儲進(jìn)位

\color{#F00000}{extra_rc}:當(dāng)表示該對象的引用計(jì)數(shù)值,實(shí)際上是引用計(jì)數(shù)值減 1, 例如,如果對象的引用計(jì)數(shù)為 10,那么 extra_rc 為 9。如果引用計(jì)數(shù)大于 10, 則需要使用到下面的 has_sidetable_rc。

其中 \color{#F00000}{shiftcls}就是當(dāng)前的類信息,因?yàn)榇蟛糠?img class="math-inline" src="https://math.jianshu.com/math?formula=%5Ccolor%7B%23F00000%7D%7B%E8%87%AA%E5%AE%9A%E4%B9%89%E7%9A%84%E7%B1%BB%7D" alt="\color{#F00000}{自定義的類}" mathimg="1">都是 \color{#F00000}{nonpointer}的 ,說白了64位都存了滿滿的信息,并\color{#F00000}{不是單單只存了類指針}

原理探索
驗(yàn)證 isa 指針 位置
  • 打斷點(diǎn)調(diào)試 執(zhí)行 isa_t newisa(0) 后:
newisa.png
  • 執(zhí)行:newisa.bits = ISA_MAGIC_VALUE 后
newisa.bits.png

isa 的過程是 calloc 指針 把 當(dāng)前的類關(guān)聯(lián)起來

  • 執(zhí)行:newisa.shiftcls = (unitprt_t)cls >> 3 ,右移三位,是為了抹掉 nonpointer...三位的值
shiftcls.png

以下驗(yàn)證關(guān)聯(lián) shiftcls 存的類信息

3.isa 與 類的關(guān)聯(lián)驗(yàn)證

  • 讀取出 shiftcls 的值(bits除shiftcls外其余位置0) 有兩種方式
    一種:地址 右移3位,再左移20位,再右移17位
    二種:object_getClass(id obj) -> getIsa() -> ISA() 中的 代碼 isa.bits & ISA_MASK
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); // 取出 shiftcls 的值
#endif
}

如圖:

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

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