教你深刻理解Runtime機制

什么是Runtime力细?

概念

Objective-C是基于C語言加入面向?qū)ο筇匦院拖⑥D(zhuǎn)發(fā)機制的動態(tài)語言,這就是說它不僅需要一個編譯器诅蝶,還需要Runtime系統(tǒng)動態(tài)的創(chuàng)建類和對象候生,進行消息發(fā)送和轉(zhuǎn)發(fā)。關(guān)于Runtime概念眾說紛紜湾趾。理解Runtime芭商,我們從源碼開始.... 源碼介紹 Runtime在實際開發(fā)中,其實就是一組C語言函數(shù)搀缠。

官方介紹:官方文檔
The Objective-C language defers as many decisions as it can from compile time and link time to runtime. Whenever possible, it does things dynamically. This means that the language requires not just a compiler, but also a runtime system to execute the compiled code. The runtime system acts as a kind of operating system for the Objective-C language; it’s what makes the language work.

怎么理解這句話呢铛楣?盡量將決定放到運行的時候,而不是在編譯和鏈接過程...如圖所示


9fLXqO.png

Clang 是什么鬼艺普?

Clang是一個C語言簸州、C++、Objective-C歧譬、Objective-C++語言的輕量級編譯器岸浑。

官方介紹:官方文檔

clang is a C, C++, and Objective-C compiler which encompasses preprocessing, parsing, optimization, code generation, assembly, and linking.

源代碼:main.m

//
//  main.m
//  YJDoctor
//
//  Created by YJHou on 2015/10/25.
//  Copyright ? 2015年 houmanager@hotmail.com. All rights reserved.
//

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        NSObject *obj = [[NSObject alloc] init];
        NSLog(@"-->%@", obj);
    }
    return 0;
}

編譯器:

clang -rewrite-objc main.m

生成了mian.cpp文件,打開查看源碼:

int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 

        NSObject *obj = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"));
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_dr_k2drvnh548q5293brg9wmgzc0000gn_T_main_2a142f_mi_0, obj);
    }
    return 0;
}

似乎看到了Runtime的影子:objc_msgSend瑰步、objc_getClass矢洲、sel_registerName...

尋找Runtime的來源

打開資源地址:/usr/include/objc 會發(fā)現(xiàn)如下文件:

9fX2HU.png

為什么要熟悉掌握Runtime機制?

Runtime 在實際開發(fā)中缩焦,會經(jīng)常用到嗎桑李?這個答案是肯定的盅蝗。但是Runtime用的好不好在于理解程度,理解的好代碼質(zhì)量高效實用;用的不好勃痴,容易自己造坑。在實際開發(fā)中排宰,我并不是推薦大家熟悉靈活的運用底層的東西媚值,而是熟悉知道底層的運行機制。要不已經(jīng)封裝好看又好用的API干啥使。

Runtime 具體都干啥使用靴拱?

比如:動態(tài)添加屬性垃喊、動態(tài)添加方法、方法交換袜炕、字典模型轉(zhuǎn)換

面試經(jīng)歷: 曾經(jīng)一次面試本谜,面試官說類別能不能設(shè)置屬性?咋一聽偎窘,條件反射類別還能設(shè)置屬性乌助,什么鬼,后來一想面試官問的是怎么給類別添加屬性吧陌知,用詞準確很重要他托,添加和設(shè)置概念是不同的。面試官馬上更正是添加不是設(shè)置仆葡。

深刻理解Runtime的底層原理是什么樣子的赏参?

首先了解Runtime的數(shù)據(jù)結(jié)構(gòu)

打開runtime.h會看到數(shù)據(jù)結(jié)構(gòu)如圖所示:

9hCZwt.png
  • id : typedef struct objc_object *id;
  • SEL : typedef struct objc_selector *SEL;
  • Class : Class 也有一個 isa 指針,指向其所屬的元類(meta)沿盅。
  • super_class:指向其超類把篓。
  • name:是類名。
  • version:是類的版本信息腰涧。
  • info:是類的詳情韧掩。
  • instance_size:是該類的實例對象的大小。
  • ivars:指向該類的成員變量列表窖铡。
  • methodLists:指向該類的實例方法列表疗锐,它將方法選擇器和方法實現(xiàn)地址聯(lián)系起來。methodLists 是指向 ·objc_method_list 指針的指針费彼,也就是說可以動態(tài)修改
  • methodLists 的值來添加成員方法滑臊,這也是 Category 實現(xiàn)的原理,同樣解釋了 Category 不能添加屬性的原因敌买。
  • cache:Runtime 系統(tǒng)會把被調(diào)用的方法存到 cache 中(理論上講一個方法如果被調(diào)用简珠,那么它有可能今后還會被調(diào)用),下次查找的時候效率更高虹钮。
  • protocols:指向該類的協(xié)議列表(對象方法列表的擴展)聋庵。

理解底層原理要從這三張圖說起:

Messaging 官方介紹

9fz4Z6.png
  1. 剛開始clang mian.m文件可以看出,Runtime System 會將方法調(diào)用轉(zhuǎn)化為消息發(fā)送(objc_msgSend), 并把方法的調(diào)用者和方法選擇器當做參數(shù)傳遞芙粱。
  2. 此時祭玉,方法調(diào)用者會通過isa指針來找到其所屬的類,然后在cache或者methodLists中查找該方法春畔,如果能找到就跳到對應的方法(IMP)中執(zhí)行脱货。
  3. 如果在類中沒有找到該方法岛都,會檢查本類是否有動態(tài)加載的方法來處理該消息,如果還是沒有振峻,通過super_class網(wǎng)上一級父類查找, 如果一直到NSObject都沒找到該方法的話臼疫,這種情況,就該消息轉(zhuǎn)發(fā)上場了扣孟。
  4. 從數(shù)據(jù)結(jié)構(gòu)中看到烫堤,methodLists 指向該類的實例方法列表,那么類方法在哪里凤价?類方法存儲在元類中鸽斟,Class通過isa指針即可找到所屬的元類。

Dynamic Method Resolution 官方介紹

9h9cRg.png

從有圖可以看出利诺,ObjC 類本身同時也是一個對象富蓄,為了處理類和對象的關(guān)系,runtime 庫創(chuàng)建了一種叫做元類 (Meta Class) 的東西慢逾,類對象所屬類型就叫做元類立倍,它用來表述類對象本身所具備的元數(shù)據(jù)。類方法就定義于此處氛改,因為這些方法可以理解成類對象的實例方法帐萎。

每個類僅有一個類對象比伏,而每個類對象僅有一個與之相關(guān)的元類胜卤。當你發(fā)出一個類似 [NSObject alloc] 的消息時,你事實上是把這個消息發(fā)給了一個類對象 (Class Object) 赁项,這個類對象必須是一個元類的實例葛躏,而這個元類同時也是一個根元類 (root meta class) 的實例。所有的元類最終都指向根元類為其超類悠菜。所有的元類的方法列表都有能夠響應消息的類方法舰攒。所以當 [NSObject alloc] 這條消息發(fā)給類對象的時候,objc_msgSend() 會去它的元類里面去查找能夠響應消息的方法悔醋,如果找到了摩窃,然后對這個類對象執(zhí)行方法調(diào)用。

在Runtime System沒有在本類的method_lists沒有找到匹配的實現(xiàn)方法時芬骄,我們可以動態(tài)的添加一個方法猾愿,這是開始進行消息轉(zhuǎn)發(fā)(messaging forward)前的第一階段,例如我們用@dynamic關(guān)鍵字在類的實現(xiàn)文件中修飾一個屬性:這表明我們會為這個屬性動態(tài)提供存取方法账阻,編譯器不會默認為我們生成setPropertyName:和propertyName方法蒂秘,而需要我們動態(tài)提供。

同樣我們可以通過分別重載resolveInstanceMethod:和resolveClassMethod:方法分別添加實例方法實現(xiàn)和類方法實現(xiàn)淘太。因為當 Runtime 系統(tǒng)在Cache和方法分發(fā)表中來給程序員一次動態(tài)添加方法實現(xiàn)的機會姻僧。我們需要用class_addMethod函數(shù)完成向特定類添加特定方法實現(xiàn)的操作:

Message Forwarding 官方介紹

9h9TiT.jpg

消息轉(zhuǎn)發(fā)分為兩大階段规丽。

  • 第一階段先征詢接收者,所屬的類撇贺,看其是否能動態(tài)添加方法赌莺,以處理當前這個“未知的選擇子”,這叫做“動態(tài)方法解析”松嘶。
  • 第二階段涉及“完整的消息轉(zhuǎn)發(fā)機制(full forwarding mechanism)”如果運行期系統(tǒng)已經(jīng)執(zhí)行完第一階段雄嚣,此時,運行期系統(tǒng)會請求我接收者以其它手段來處理消息喘蟆』荷可以細分3小步。

1.首先查找有沒有replacement receiver進行處理蕴轨。若無;

2.運行期系統(tǒng)把Selector相關(guān)信息封裝到NSInvocation對象中;

3.再給一次機會港谊,若依舊未處理則讓NSObject調(diào)用doNotReconizeSelector:

具體看代碼所示:

9hA4eJ.png

由此我們可以看到,object_getClass返回的其實是class的metaClass橙弱,即Class這個類對象的類歧寺,這個概念有點繞。梳理一下:Person這么一個類(0x1000f53c8)棘脐,它的isa指針指向其元類(地址0x1000f53f0)斜筐,這個元類的isa指針指向基類NSObject的元類,即根元類(0x1a7919ec8)蛀缝,再遞進一層可以發(fā)現(xiàn)顷链,根元類的isa指針指向自己,這樣就形成了一個完整的閉環(huán)屈梁。

另外objc_getClass 是什么鬼嗤练?

9hEFl8.png

由此可知objc_getClass方法只是單純地返回了Class,而非isa指針指向的Class在讶。

Runtime的應用場景有什么煞抬?

  • 實現(xiàn)第一個場景:跟蹤程序每個ViewController展示給用戶的次數(shù),可以通過Method Swizzling替換ViewDidAppear初始方法构哺。創(chuàng)建一個UIViewController的分類革答,重寫自定義的ViewDidAppear方法,并在其+load方法中實現(xiàn)ViewDidAppear方法的交換
  • 開發(fā)中常需要在不改變某個類的前提下為其添加一個新的屬性曙强,尤其是為系統(tǒng)的類添加新的屬性残拐,這個時候就可以利用Runtime的關(guān)聯(lián)對象(Associated Objects)來為分類添加新的屬性了
  • 三實現(xiàn)字典的模型和自動轉(zhuǎn)換,優(yōu)秀的JSON轉(zhuǎn)模型第三方庫JSONModel旗扑、YYModel等都利用runtime對屬性進行獲取蹦骑,賦值等操作,要比KVC進行模型轉(zhuǎn)換更加強大臀防,更有效率眠菇。閱讀YYModel的源碼可以看出边败,YY大神對NSObject的內(nèi)容進行了又一次封裝,添加了許多描述內(nèi)容捎废。其中YYClassInfo是對Class進行了再次封裝笑窜,而YYClassIvarInfo、YYClassMethodInfo登疗、YYClPropertyInfo分別是對Class的Ivar排截、Method和property進行了封裝和描述。在提取Class的相關(guān)信息時都運用了Runtime辐益。
  • JSPatch替換已有的OC方法實行断傲,具體內(nèi)容請參看相關(guān)文檔。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末智政,一起剝皮案震驚了整個濱河市认罩,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌续捂,老刑警劉巖垦垂,帶你破解...
    沈念sama閱讀 222,681評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異牙瓢,居然都是意外死亡劫拗,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評論 3 399
  • 文/潘曉璐 我一進店門矾克,熙熙樓的掌柜王于貴愁眉苦臉地迎上來页慷,“玉大人,你說我怎么就攤上這事聂渊〔罟海” “怎么了?”我有些...
    開封第一講書人閱讀 169,421評論 0 362
  • 文/不壞的土叔 我叫張陵汉嗽,是天一觀的道長。 經(jīng)常有香客問我找蜜,道長饼暑,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,114評論 1 300
  • 正文 為了忘掉前任洗做,我火速辦了婚禮弓叛,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘诚纸。我一直安慰自己撰筷,他們只是感情好,可當我...
    茶點故事閱讀 69,116評論 6 398
  • 文/花漫 我一把揭開白布畦徘。 她就那樣靜靜地躺著毕籽,像睡著了一般抬闯。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上关筒,一...
    開封第一講書人閱讀 52,713評論 1 312
  • 那天溶握,我揣著相機與錄音,去河邊找鬼蒸播。 笑死睡榆,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的袍榆。 我是一名探鬼主播胀屿,決...
    沈念sama閱讀 41,170評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼包雀!你這毒婦竟也來了碉纳?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,116評論 0 277
  • 序言:老撾萬榮一對情侶失蹤馏艾,失蹤者是張志新(化名)和其女友劉穎劳曹,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體琅摩,經(jīng)...
    沈念sama閱讀 46,651評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡铁孵,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,714評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了房资。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蜕劝。...
    茶點故事閱讀 40,865評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖轰异,靈堂內(nèi)的尸體忽然破棺而出岖沛,到底是詐尸還是另有隱情,我是刑警寧澤搭独,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布婴削,位于F島的核電站,受9級特大地震影響牙肝,放射性物質(zhì)發(fā)生泄漏唉俗。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,211評論 3 336
  • 文/蒙蒙 一配椭、第九天 我趴在偏房一處隱蔽的房頂上張望虫溜。 院中可真熱鬧,春花似錦股缸、人聲如沸衡楞。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,699評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽瘾境。三九已至歧杏,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間寄雀,已是汗流浹背得滤。 一陣腳步聲響...
    開封第一講書人閱讀 33,814評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留盒犹,地道東北人懂更。 一個月前我還...
    沈念sama閱讀 49,299評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像急膀,于是被迫代替她去往敵國和親沮协。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,870評論 2 361

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

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉卓嫂,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,732評論 0 9
  • 這篇文章完全是基于南峰子老師博客的轉(zhuǎn)載 這篇文章完全是基于南峰子老師博客的轉(zhuǎn)載 這篇文章完全是基于南峰子老師博客的...
    西木閱讀 30,569評論 33 466
  • 參考鏈接: http://www.cnblogs.com/ioshe/p/5489086.html 簡介 Runt...
    樂樂的簡書閱讀 2,138評論 0 9
  • 原文出處:南峰子的技術(shù)博客 Objective-C語言是一門動態(tài)語言慷暂,它將很多靜態(tài)語言在編譯和鏈接時期做的事放到了...
    _燴面_閱讀 1,235評論 1 5
  • 轉(zhuǎn)載:http://yulingtianxia.com/blog/2014/11/05/objective-c-r...
    F麥子閱讀 736評論 0 2