前言
繼 類的底層原理(一) 的探索后,已理解 isa指針指向
和 類的結(jié)構(gòu)
柄驻。下面繼續(xù)探索類的底層原理,并做相應(yīng)的補充焙压。
準(zhǔn)備工作
WWDC-關(guān)于 runtime
的改進(jìn)優(yōu)化
成員變量的底層原理
在分析 類的底層原理(一) 時鸿脓,只分析了 properties
和 methods
。
properties
和methods
都在class_rw_t
中涯曲。而成員變量
ivars
存在于class_rw_t
的ro()
中野哭,也就是class_ro_t
。
class_ro_t
結(jié)構(gòu)如下:
案例一
通過案例分析 ivars
幻件,添加如下代碼 :
打印結(jié)果:
此時拨黔,ivars
都存在于 ivar_list_t
中,使用C++數(shù)組 get()
獲取每個成員變量绰沥。
成員變量篱蝇、屬性、實例變量的區(qū)別
成員變量在類的
{}
中徽曲,以基本數(shù)據(jù)類型
聲明的變量态兴,例如:NSString、int疟位、float瞻润、double、char、bool绍撞。屬性是用
@property
修飾的正勒,在底層會變成_
方式的成員變量
,也會自動生成get
和set
方法傻铣。屬性 = _成員變量 + set + get
實例變量是以
對象類型
聲明的 (特殊的成員變量)章贞,例如NSObject *p
,p
就是實例變量非洲。
案例二
1. 創(chuàng)建一個 Project
鸭限,添加如下代碼:
2.通過 clang
編譯并查看編譯文件:
$ clang -rewrite-objc main.m -o main.cpp
屬性在編譯時會變成
_
的成員變量,并生成對應(yīng)的set
和get
方法两踏。
但是其
set
實現(xiàn)方式卻不一樣:
name
是通過objc_setProperty
方法實現(xiàn)的而
address
和age
則是通過內(nèi)存地址偏移的方式存儲的
為什么會不一樣败京?name
是 copy
修飾,猜想是和 copy
修飾符有關(guān)梦染。
分析 objc_setProperty
為什么會有 objc_setProperty
的存在赡麦?
當(dāng)創(chuàng)建一個
屬性
時,調(diào)用set
存數(shù)據(jù)時帕识,不可能每創(chuàng)建一個屬性
就在底層生成一個對應(yīng)的set
方法泛粹,這樣對內(nèi)存開銷太大了。于是就有了objc_setProperty
方法肮疗,不管上層是什么set
方法晶姊,統(tǒng)一調(diào)用objc_setProperty
方法。這個過程就是SEL
→IMP
過程伪货。
SEL
就是方法名字帽借,IMP
就是底層方法實現(xiàn)。
由于 objc_setProperty
是需要在編譯時直接創(chuàng)建超歌,所以 objc_setProperty
需要去 LLVM源碼
中查找砍艾。
1. 定位 objc_setProperty
方法
2. 定位 getSetPropertyFn
方法
3. 定位 GetPropertySetFunction
方法
GetPropertySetFunction
方法調(diào)用,是在switch
選擇strategy.getKind()
為PropertyImplStrategy::GetSetProperty
或者PropertyImplStrategy::SetPropertyAndExpressionGet
時執(zhí)行的巍举。那么
strategy
是什么意思脆荷?什么時候給它賦值?每個case
都有什么意義懊悯?
4. 分析 PropertyImplStrategy
5. PropertyImplStrategy
構(gòu)造函數(shù)
結(jié)論:
只要是設(shè)置了
copy
屬性蜓谋,不管是不是原子性,都沒有影響炭分,set
方法都會被重定向到objc_setProperty
桃焕。如果不設(shè)置屬性(除原子性之外),那么默認(rèn)屬性是
strong
捧毛,不會觸發(fā)objc_setProperty
观堂。
分析 objc_getProperty
同樣 objc_getProperty
也需要去 LLVM源碼
中查找让网。
1. 定位 objc_getProperty
方法
2. 定位 getGetPropertyFn
方法
3. 定位 GetPropertyGetFunction
方法
類方法的底層原理
lldb
驗證流程如下:
總結(jié):
對象方法
存儲在自身類
中
類方法
存儲在元類
中,并且以對象方法
存在于元類
中师痕。沒有所謂的
類方法
之說溃睹,所有的方法都是對象方法
,其底層都是函數(shù)
胰坟。
補充:類型編碼 TypeEncoding
在上面的 main.cpp
中因篇,搜索 setName
發(fā)現(xiàn)有一些奇奇怪怪的符號。
其中 "v24@0:8@16"
笔横、"@16@0:8"
其實是 類型編碼
竞滓。下面具體看一下什么是 類型編碼
。
官網(wǎng)關(guān)于 TypeEncoding 的解釋吹缔。歸根結(jié)底商佑,其實就是對照著下面的
符號表
去分析編碼代碼。
那么以上面 setName
為例涛菠,具體分析 v24@0:8@16
的實際意義莉御。
補充:面試題
1. 為什么獲取 元類
的 類方法
也可以得到 類方法
撇吞?(已證明:類方法
以 對象方法
形式存儲在 元類
中)
打印結(jié)果如下:
分析底層方法:
分析得出:所謂的
類方法
其實就是獲取元類
的對象方法
俗冻。
分析 getMeta()
方法:
分析得出:如果是
元類
,則返回元類本身
牍颈,否則返回元類isa
迄薄。這就是為什么獲取元類
的類方法
也可以得到類方法
的原因。
2. 關(guān)于 isKindOfClass
案例分析:
打印結(jié)果:
并且也在源碼中找到 isMemberOfClass
和 isKindOfClass
方法煮岁,也打上斷點讥蔽,來具體分析其原理:
但是執(zhí)行過程中,卻沒有執(zhí)行 isKindOfClass
方法画机,只執(zhí)行了 isMemberOfClass
方法冶伞。先來分析 isMemberOfClass
isMemberOfClass
分析源碼如下:
+ isMemberOfClass:
是獲取類的元類
與類
進(jìn)行比較
- isMemberOfClass:
是獲取類對象
與類
進(jìn)行比較
分析上圖代碼:
-
re1
是NSObject
調(diào)用+ isMemberOfClass:
與NSObject
比較。因此NSObject元類
與NSObject
并不相等步氏,所以是0响禽。 -
re3
是ZLObject
調(diào)用+ isMemberOfClass:
與ZLObject
比較。因此ZLObject元類
與ZLObject
并不相等荚醒,所以是0芋类。 -
re5
是NSObject對象
調(diào)用- isMemberOfClass:
與NSObject
比較。因此NSObject對象
的類是NSObject
界阁,與NSObject
相等侯繁,所以是1。 -
re7
是ZLObject對象
調(diào)用- isMemberOfClass:
與ZLObject
比較泡躯。因此ZLObject對象
的類是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)部都會重定向到 objc_opt_isKindOfClass
這個方法。因為 類
其本質(zhì)也是一個對象翔冀,我們稱之為 類對象
导街。所以obj每次都有值。
分析源碼如下:
如果是類:首先獲取類的
元類
與類
比較纤子。相等則返回true搬瑰。如果不相等,再獲取類的父類
與類
比較控硼,相等則返回true泽论。否則循環(huán)找父類
與類
比較,直到獲取到的父類為nil
卡乾,依舊沒有找到則返回false翼悴。如果是實例對象,首先獲取
類
與類
比較幔妨。相等則返回true鹦赎。否則和類
的步驟一樣。
分析上圖代碼:
-
re2
是NSObject
的類误堡,獲取元類
與NSObject
不等古话,繼續(xù)尋找獲取元類的父類
為NSObject
與NSObject
相等,返回1锁施。 -
re4
是ZLObject
的類陪踩,獲取元類
與ZLObject
不等,繼續(xù)尋找獲取元類的父類
為NSObject的元類
依舊不等沾谜,繼續(xù)往上NSObject元類的父類
為NSObject
依舊不等膊毁,再往上就是nil
,最后返回0基跑。 -
re6
是NSObject對象
婚温,獲取類
為NSObject
,與NSObject相等媳否,返回1栅螟。 -
re8
是ZLObject對象
荆秦,獲取類
為ZLObject
,與NSObject相等步绸,返回1。