前言
繼 類的底層原理(一) 的探索后,已理解 isa指針指向 和 類的結(jié)構(gòu) 。下面繼續(xù)探索類的底層原理,并做相應(yīng)的補(bǔ)充。
準(zhǔn)備工作
WWDC-關(guān)于 runtime 的改進(jìn)優(yōu)化
成員變量的底層原理
在分析 類的底層原理(一) 時(shí),只分析了 properties 和 methods。
properties和methods都在class_rw_t中。而成員變量
ivars存在于class_rw_t的ro()中,也就是class_ro_t。

class_ro_t 結(jié)構(gòu)如下:

案例一
通過案例分析 ivars ,添加如下代碼 :

打印結(jié)果:

此時(shí),ivars 都存在于 ivar_list_t 中,使用C++數(shù)組 get() 獲取每個(gè)成員變量。

成員變量、屬性、實(shí)例變量的區(qū)別
成員變量在類的
{}中,以基本數(shù)據(jù)類型聲明的變量,例如:NSString、int、float、double、char、bool。屬性是用
@property修飾的,在底層會(huì)變成_方式的成員變量,也會(huì)自動(dòng)生成get和set方法。屬性 = _成員變量 + set + get實(shí)例變量是以
對(duì)象類型聲明的 (特殊的成員變量),例如NSObject *p,p就是實(shí)例變量。
案例二
1. 創(chuàng)建一個(gè) Project,添加如下代碼:

2.通過 clang 編譯并查看編譯文件:
$ clang -rewrite-objc main.m -o main.cpp

屬性在編譯時(shí)會(huì)變成
_的成員變量,并生成對(duì)應(yīng)的set和get方法。

但是其
set實(shí)現(xiàn)方式卻不一樣:
name是通過objc_setProperty方法實(shí)現(xiàn)的而
address和age則是通過內(nèi)存地址偏移的方式存儲(chǔ)的
為什么會(huì)不一樣?name 是 copy 修飾,猜想是和 copy 修飾符有關(guān)。
分析 objc_setProperty
為什么會(huì)有 objc_setProperty 的存在?
當(dāng)創(chuàng)建一個(gè)
屬性時(shí),調(diào)用set存數(shù)據(jù)時(shí),不可能每創(chuàng)建一個(gè)屬性就在底層生成一個(gè)對(duì)應(yīng)的set方法,這樣對(duì)內(nèi)存開銷太大了。于是就有了objc_setProperty方法,不管上層是什么set方法,統(tǒng)一調(diào)用objc_setProperty方法。這個(gè)過程就是SEL→IMP過程。
SEL就是方法名字,IMP就是底層方法實(shí)現(xiàn)。
由于 objc_setProperty 是需要在編譯時(shí)直接創(chuàng)建,所以 objc_setProperty 需要去 LLVM源碼 中查找。
1. 定位 objc_setProperty 方法

2. 定位 getSetPropertyFn 方法

3. 定位 GetPropertySetFunction 方法

GetPropertySetFunction方法調(diào)用,是在switch選擇strategy.getKind()為PropertyImplStrategy::GetSetProperty或者PropertyImplStrategy::SetPropertyAndExpressionGet時(shí)執(zhí)行的。那么
strategy是什么意思?什么時(shí)候給它賦值?每個(gè)case都有什么意義?
4. 分析 PropertyImplStrategy

5. PropertyImplStrategy 構(gòu)造函數(shù)

結(jié)論:
只要是設(shè)置了
copy屬性,不管是不是原子性,都沒有影響,set方法都會(huì)被重定向到objc_setProperty。如果不設(shè)置屬性(除原子性之外),那么默認(rèn)屬性是
strong,不會(huì)觸發(fā)objc_setProperty。
分析 objc_getProperty
同樣 objc_getProperty 也需要去 LLVM源碼 中查找。
1. 定位 objc_getProperty 方法

2. 定位 getGetPropertyFn 方法

3. 定位 GetPropertyGetFunction 方法

類方法的底層原理
lldb 驗(yàn)證流程如下:

總結(jié):
對(duì)象方法存儲(chǔ)在自身類中
類方法存儲(chǔ)在元類中,并且以對(duì)象方法存在于元類中。沒有所謂的
類方法之說(shuō),所有的方法都是對(duì)象方法,其底層都是函數(shù)。
補(bǔ)充:類型編碼 TypeEncoding
在上面的 main.cpp 中,搜索 setName 發(fā)現(xiàn)有一些奇奇怪怪的符號(hào)。

其中 "v24@0:8@16"、"@16@0:8" 其實(shí)是 類型編碼 。下面具體看一下什么是 類型編碼。
官網(wǎng)關(guān)于 TypeEncoding 的解釋。歸根結(jié)底,其實(shí)就是對(duì)照著下面的
符號(hào)表去分析編碼代碼。

那么以上面 setName 為例,具體分析 v24@0:8@16 的實(shí)際意義。

補(bǔ)充:面試題
1. 為什么獲取 元類 的 類方法 也可以得到 類方法?(已證明:類方法 以 對(duì)象方法 形式存儲(chǔ)在 元類 中)


打印結(jié)果如下:

分析底層方法:

分析得出:所謂的
類方法其實(shí)就是獲取元類的對(duì)象方法。
分析 getMeta() 方法:

分析得出:如果是
元類,則返回元類本身,否則返回元類isa。這就是為什么獲取元類的類方法也可以得到類方法的原因。
2. 關(guān)于 isKindOfClass
案例分析:

打印結(jié)果:

并且也在源碼中找到 isMemberOfClass 和 isKindOfClass 方法,也打上斷點(diǎn),來(lái)具體分析其原理:

但是執(zhí)行過程中,卻沒有執(zhí)行 isKindOfClass 方法,只執(zhí)行了 isMemberOfClass 方法。先來(lái)分析 isMemberOfClass
isMemberOfClass
分析源碼如下:
+ isMemberOfClass:是獲取類的元類與類進(jìn)行比較
- isMemberOfClass:是獲取類對(duì)象與類進(jìn)行比較
分析上圖代碼:
-
re1是NSObject調(diào)用+ isMemberOfClass:與NSObject比較。因此NSObject元類與NSObject并不相等,所以是0。 -
re3是ZLObject調(diào)用+ isMemberOfClass:與ZLObject比較。因此ZLObject元類與ZLObject并不相等,所以是0。 -
re5是NSObject對(duì)象調(diào)用- isMemberOfClass:與NSObject比較。因此NSObject對(duì)象的類是NSObject,與NSObject相等,所以是1。 -
re7是ZLObject對(duì)象調(diào)用- isMemberOfClass:與ZLObject比較。因此ZLObject對(duì)象的類是ZLObject,與ZLObject相等,所以是1。
isKindOfClass
上述案例中,isKindOfClass 沒有執(zhí)行,只有 isMemberOfClass 相關(guān)方法執(zhí)行了。這是什么原因呢?
具體分析匯編才知道,isKindOfClass沒有執(zhí)行的原因是底層執(zhí)行了 objc_opt_isKindOfClass 方法。

具體分析 objc_opt_isKindOfClass:

在上面的代碼中,不管上層調(diào)用的是 + isKindOfClass: 還是 - isKindOfClass:,內(nèi)部都會(huì)重定向到 objc_opt_isKindOfClass 這個(gè)方法。因?yàn)?類 其本質(zhì)也是一個(gè)對(duì)象,我們稱之為 類對(duì)象。所以obj每次都有值。
分析源碼如下:
如果是類:首先獲取類的
元類與類比較。相等則返回true。如果不相等,再獲取類的父類與類比較,相等則返回true。否則循環(huán)找父類與類比較,直到獲取到的父類為nil,依舊沒有找到則返回false。如果是實(shí)例對(duì)象,首先獲取
類與類比較。相等則返回true。否則和類的步驟一樣。
分析上圖代碼:
-
re2是NSObject的類,獲取元類與NSObject不等,繼續(xù)尋找獲取元類的父類為NSObject與NSObject相等,返回1。 -
re4是ZLObject的類,獲取元類與ZLObject不等,繼續(xù)尋找獲取元類的父類為NSObject的元類依舊不等,繼續(xù)往上NSObject元類的父類為NSObject依舊不等,再往上就是nil,最后返回0。 -
re6是NSObject對(duì)象,獲取類為NSObject,與NSObject相等,返回1。 -
re8是ZLObject對(duì)象,獲取類為ZLObject,與NSObject相等,返回1。