iOS底層原理 - 探尋Runtime本質(zhì) 之 Class

面試題引發(fā)的思考:

Q: 簡(jiǎn)述Class原理丝里?

  • 通過(guò) bits & FAST_DATA_MASK 可以得到 class_rw_t 結(jié)構(gòu)體曲初;
  • class_rw_t結(jié)構(gòu)體 中存放 方法列表、屬性列表杯聚、協(xié)議列表 等臼婆,
    并包括一個(gè)指向 class_ro_t結(jié)構(gòu)體 的指針ro
  • class_ro_t結(jié)構(gòu)體 中存放 方法列表幌绍、屬性列表颁褂、協(xié)議列表 等故响,
    并包括 成員變量列表;包括所有 類的初始內(nèi)容颁独。

Q: 簡(jiǎn)述方法緩存cache_t原理彩届?

  • iOS采用方法緩存cache_t,用散列表(哈希表)緩存曾經(jīng)調(diào)用過(guò)的方法奖唯,可以提高方法的查找速度惨缆。
    調(diào)用方法時(shí)優(yōu)先去 緩存 中查找此方法;
    緩存 中沒(méi)有再去 類對(duì)象 中查找丰捷;
    然后將其加入 緩存 中方便下次調(diào)用坯墨。

1. Class結(jié)構(gòu)的本質(zhì)

上一章對(duì)isa結(jié)構(gòu)的本質(zhì)做了探究,下面探究Class的內(nèi)部結(jié)構(gòu)病往。

iOS底層原理 - OC對(duì)象的本質(zhì)(二)可知:

isa指針指向圖示
  • class對(duì)象和meta-class對(duì)象結(jié)構(gòu)相似捣染,meta-class對(duì)象是特殊的class對(duì)象;
  • 關(guān)于存儲(chǔ)方法:
    class對(duì)象存儲(chǔ)對(duì)象方法停巷,meta-class對(duì)象存儲(chǔ)類方法耍攘。

(1) Class的結(jié)構(gòu)

OC源碼可以找出Class的結(jié)構(gòu):

Class的結(jié)構(gòu)

Class的結(jié)構(gòu)可知:

  • 通過(guò)bits & FAST_DATA_MASK可以得到class_rw_t結(jié)構(gòu)體;
  • class_rw_t結(jié)構(gòu)體中存放方法列表畔勤、屬性列表蕾各、協(xié)議列表等,并包括一個(gè)指向class_ro_t結(jié)構(gòu)體的指針ro庆揪;rwreadwrite可讀可寫(xiě))
  • class_ro_t結(jié)構(gòu)體中存放類的初始內(nèi)容式曲,包括方法列表、屬性列表缸榛、協(xié)議列表等吝羞,并包括成員變量列表。roreadonly只讀)

(2) class_rw_t 結(jié)構(gòu)

class_rw_t結(jié)構(gòu)體中存放方法列表(method_array_t類型)内颗、屬性列表 (property_array_t類型)钧排、協(xié)議列表(protocol_array_t類型),接下來(lái)分析三者的結(jié)構(gòu):

method_array_t均澳、property_array_t恨溜、protocol_array_t 結(jié)構(gòu)

通過(guò)以上源碼可知:
method_array_tproperty_array_t负懦、protocol_array_t結(jié)構(gòu)相同筒捺;

下面只對(duì)method_array_t的內(nèi)部結(jié)構(gòu)進(jìn)行分析,property_array_tprotocol_array_t的可以類推纸厉。

method_array_t內(nèi)部結(jié)構(gòu)

method_array_t內(nèi)部結(jié)構(gòu)可知:
method_array_t是一個(gè)二維數(shù)組,每個(gè)元素是method_list_t五嫂;
method_list_t是一維數(shù)組颗品,每個(gè)元素是method_t肯尺;
class_rw_t里面的methodsproperties躯枢、protocols是二維數(shù)組则吟,是可讀可寫(xiě)的,包含了類的初始內(nèi)容锄蹂、分類的內(nèi)容氓仲。


(3) class_ro_t結(jié)構(gòu)

class_ro_t結(jié)構(gòu)體中存放方法列表(method_list_t類型)、屬性列表 (property_list_t類型)得糜、協(xié)議列表(protocol_list_t類型)敬扛,所以method_list_t的內(nèi)部結(jié)構(gòu)如下:

method_list_t內(nèi)部結(jié)構(gòu)

method_list_t內(nèi)部結(jié)構(gòu)可知:
method_list_t是一維數(shù)組,每個(gè)元素是method_t朝抖;
class_ro_t里面的baseMethodList啥箭、baseProtocolsivars治宣、baseProperties是一維數(shù)組急侥,是只讀的,包含了類的初始內(nèi)容侮邀。


(4) class_rw_t結(jié)構(gòu)與class_ro_t結(jié)構(gòu)

iOS底層原理 - 探尋Category本質(zhì)(一)可知:

class_rw_t結(jié)構(gòu)體通過(guò)attachList函數(shù)內(nèi)的memmovememcpy操作將所有分類的對(duì)象方法附加到類對(duì)象的方法列表中坏怪。

通過(guò)源碼進(jìn)入realizeClass函數(shù),查看class_rw_tclass_ro_t兩者之間的關(guān)系:

realizeClass函數(shù)

realizeClass函數(shù)可知:

類的初始信息(方法绊茧、屬性铝宵、協(xié)議、成員變量等)存放在class_ro_t中按傅;
程序運(yùn)行時(shí)捉超,class_ro_t中的列表和分類中的列表合并起來(lái)存放在class_rw_t中。


(5) method_t結(jié)構(gòu)

由以上分析可知:
class_rw_t結(jié)構(gòu)體和class_ro_t結(jié)構(gòu)體的方法列表唯绍,其最小單位都是method_t結(jié)構(gòu)體拼岳。

通過(guò)源碼進(jìn)入method_t結(jié)構(gòu)體:

method_t結(jié)構(gòu)

由源碼可知:method_t結(jié)構(gòu)體包含三個(gè)成員變量,接下對(duì)三者進(jìn)行分析:

1> SEL name;函數(shù)名
  • SEL代表方法/函數(shù)名况芒,即選擇器惜纸,底層結(jié)構(gòu)和char指針類似;
    通過(guò)@selector()sel_registerName()獲得绝骚;
    通過(guò)sel_getName()NSStringFromSelector()SEL轉(zhuǎn)成字符串耐版;
  • 不同類中相同名字的方法,所對(duì)應(yīng)的方法選擇器是相同的压汪。

驗(yàn)證如下:

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        SEL sel1 = @selector(init);
        SEL sel2 = sel_registerName("init");
        NSLog(@"%p %p", sel1, sel2);

        const char *str1 = sel_getName(sel1);
        NSString *str2 = NSStringFromSelector(sel2);
        NSLog(@"%s %@", str1, str2);
    }
    return 0;
}

// 打印結(jié)果
Demo[1234:567890] 0x7fff7c4a6b8b 0x7fff7c4a6b8b
Demo[1234:567890] init init

打印的地址與方法名都相同粪牲,驗(yàn)證成功。

2> const char *types;編碼(返回值類型止剖,參數(shù)類型)

Person類中寫(xiě)一個(gè)方法:

// TODO: -----------------  Person類  -----------------
@interface Person : NSObject
@end

@implementation Person
- (int)testHeight:(float)height age:(int)age {
    return 0;
}
@end

Person.m文件轉(zhuǎn)為C++語(yǔ)言腺阳,找到_class_ro_t結(jié)構(gòu)體落君,因?yàn)樗嬷惖某跏夹畔ⅲ?/p>

_class_ro_t源碼

由以上源碼可知:
方法名為:testHeight:age:
編碼types為:"i24@0:8f16i20"亭引;
函數(shù)地址為:_I_Person_testHeight_age_绎速。

編碼types為:"i24@0:8f16i20",采用了iOS的@encode的指令焙蚓,該指令將具體的類型表示成字符串編碼纹冤。部分編碼如下:

Objective-C type encodings

我們通過(guò)該表進(jìn)行分析:

// 函數(shù)默認(rèn)會(huì)帶有self和_cmd兩個(gè)參數(shù)
- (int)testHeight:(float)height age:(int)age;
types - i24@0:8f16i20
types - i    24    @    0    :    8    f    16    I    20
        int        id        SEL      float      int
       返回值      self      _cmd      height     age
// 分析數(shù)字意義:
24 - 所有參數(shù)占24個(gè)字節(jié)
0 - self是從第0個(gè)字節(jié)開(kāi)始存儲(chǔ)购公,id類型占8個(gè)字節(jié)
8 - _cmd是從第8個(gè)字節(jié)開(kāi)始存儲(chǔ)萌京,SEL類型占8個(gè)字節(jié)
16 - height是從第16個(gè)字節(jié)開(kāi)始存儲(chǔ),float類型占4個(gè)字節(jié)
20 - age是從第20個(gè)字節(jié)開(kāi)始存儲(chǔ)君丁,int類型占4個(gè)字節(jié)
3> IMP imp;指向函數(shù)的指針(函數(shù)地址)

IMP的內(nèi)部實(shí)現(xiàn)如下:
typedef id _Nullable (*IMP)(id _Nonnull, SEL _Nonnull, ...);

IMP代表函數(shù)的具體實(shí)現(xiàn)枫夺,存儲(chǔ)著函數(shù)地址。


2. 方法緩存cache_t

iOS底層原理 - OC對(duì)象的本質(zhì)(二)可知:

isa指針绘闷、superclass指針指向匯總圖示

如果調(diào)用對(duì)象方法:

  • 首先通過(guò)instance的isa指向Class橡庞,
    然后在Class對(duì)象中class_rw_t結(jié)構(gòu)體的methods數(shù)組中查找方法;
  • 如果Class對(duì)象中找不到該方法印蔗,
    需要通過(guò)superclass指針找到父類的Class對(duì)象扒最,
    然后在父類的Class對(duì)象中class_rw_t結(jié)構(gòu)體的methods數(shù)組中查找方法
  • 如果父類的Class對(duì)象中找不到該方法,再次通過(guò)superclass指針找上一級(jí)的父類华嘹,如此循環(huán)吧趣。

如果一個(gè)方法需要調(diào)用許多次的話,需要循環(huán)遍歷多次以上步驟耙厚;

iOS采用方法緩存cache_t强挫,用散列表(哈希表)緩存曾經(jīng)調(diào)用過(guò)的方法,可以提高方法的查找速度薛躬。

調(diào)用方法時(shí)優(yōu)先去緩存中查找此方法俯渤,緩存中沒(méi)有再去類對(duì)象中查找,然后將其加入緩存中方便下次調(diào)用型宝。


(1) cache_t緩存方式

OC源碼可以找出cache_t的結(jié)構(gòu):

cache_t結(jié)構(gòu)

cache_t結(jié)構(gòu)可知:

  • cache_t結(jié)構(gòu)體存儲(chǔ)_buckets八匠、_mask_occupied趴酣;
  • _buckets散列表梨树,存放方法選擇器充當(dāng)?shù)?code>Key值,以及函數(shù)的內(nèi)存地址IMP岖寞;
  • _mask散列表的長(zhǎng)度減1抡四,任何數(shù)通過(guò)與_mask進(jìn)行按位與運(yùn)算之后獲得的值都會(huì)小于等于_mask,不會(huì)出現(xiàn)數(shù)組溢出的情況仗谆;
  • _occupied已經(jīng)緩存的方法數(shù)量床嫌。

cache_t結(jié)構(gòu)可以歸納如下:

cache_t結(jié)構(gòu)

接下來(lái)查看OC內(nèi)部如何處理緩存:

cache_t::find函數(shù)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末跨释,一起剝皮案震驚了整個(gè)濱河市胸私,隨后出現(xiàn)的幾起案子厌处,更是在濱河造成了極大的恐慌,老刑警劉巖岁疼,帶你破解...
    沈念sama閱讀 221,198評(píng)論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件阔涉,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡捷绒,警方通過(guò)查閱死者的電腦和手機(jī)瑰排,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)暖侨,“玉大人椭住,你說(shuō)我怎么就攤上這事∽侄海” “怎么了京郑?”我有些...
    開(kāi)封第一講書(shū)人閱讀 167,643評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)葫掉。 經(jīng)常有香客問(wèn)我些举,道長(zhǎng),這世上最難降的妖魔是什么俭厚? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,495評(píng)論 1 296
  • 正文 為了忘掉前任户魏,我火速辦了婚禮,結(jié)果婚禮上挪挤,老公的妹妹穿的比我還像新娘叼丑。我一直安慰自己,他們只是感情好扛门,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,502評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布鸠信。 她就那樣靜靜地躺著,像睡著了一般尖飞。 火紅的嫁衣襯著肌膚如雪症副。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,156評(píng)論 1 308
  • 那天政基,我揣著相機(jī)與錄音贞铣,去河邊找鬼。 笑死沮明,一個(gè)胖子當(dāng)著我的面吹牛辕坝,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播荐健,決...
    沈念sama閱讀 40,743評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼酱畅,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼琳袄!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起纺酸,我...
    開(kāi)封第一講書(shū)人閱讀 39,659評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤窖逗,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后餐蔬,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體碎紊,經(jīng)...
    沈念sama閱讀 46,200評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,282評(píng)論 3 340
  • 正文 我和宋清朗相戀三年樊诺,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了仗考。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,424評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡词爬,死狀恐怖秃嗜,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情顿膨,我是刑警寧澤锅锨,帶...
    沈念sama閱讀 36,107評(píng)論 5 349
  • 正文 年R本政府宣布,位于F島的核電站虽惭,受9級(jí)特大地震影響橡类,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜芽唇,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,789評(píng)論 3 333
  • 文/蒙蒙 一顾画、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧匆笤,春花似錦研侣、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,264評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間荔茬,已是汗流浹背厦坛。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,390評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工九杂, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,798評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像晴玖,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,435評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容