概述: runtime又叫運行時,是壹套底層C語言API,是iOS系統的核心之壹。開發者在編碼過程中,可以給任意壹個對象發送消息,在編譯階段只是確定了要向接受著發送這條消息,而接受者如何響應和處理這條消息,就要看運行時來決定了
?C語言中,在編譯器就確定要調用哪個函數,而OC的函數,屬於動態調用過程,在編譯器並不能真正決定調用哪個函數,只有在真正的運行時才會根據函數的名稱找到對應的函數來調用。OC是壹個動態語言,這意味著它不僅要壹個編譯器,也需要壹個運行時系統來動態創建類和對象、進行消息傳遞和發送
1.消息轉發
Runtime的特性主要是消息傳遞,如果消息在對象中找不到,就進行轉發。Objective-C是壹個動態語言,這意味著它不僅需要壹個編譯器,也需要壹個運行時系統來動態創建類和對象、進行消息傳遞和轉發。Runtime的核心是消息傳遞。
(1)消息傳遞的過程
壹個對象的方法[obj test],編譯器轉成消息發送objc_msgSend(obj,test),Runtime執行的流程是這樣的
a.首先通過obj的isa指針找到它的class
b.在class的method list找test
c.如果class中沒找到test,繼續往它的superclass中找
d.壹旦找到test這個函數,就去執行它的IMP
由於效率問題,每個消息都遍歷壹次objc_method_list並不合理,所以需要把經常被調用的函數緩存下來,去提高函數查詢的效率。這也就是objc_class中另壹個重要的成員objc_cache做的事情。找到test之後,將test的method_name作為key,method_imp作為value。當再次收到test消息的時候,可以直接在cache裏找。
類對象(objc_class)
Objective-C類是由Class類型來表示的,它實際上是壹個指向objc_class結構體的指針。struct objc_class結構體裏面定義了很多變量。結構體裏保存了指向父類的指針、類的名字(name)、版本、實例變量列表(ivars)、方法列表(methodLists)、緩存(cache)、遵守的協議列表(protocols),由此可見,類對象就是壹個結構體struct objc_class,這個結構體存放的數據就是元數據
理解Runtime就是理解iOS在運行時他的數據存儲以及他的類、實例、類對象、元類她們之間的關系和作用。
(2)消息轉發機制
歸根到底,Objective-C中所有的方法調用本質就是向對象發送消息
1.類中創建方法-(void)test
2.iOS系統為這個方法創建壹個編號,SEL(test)並添加到方法列表裏面
3.當調用這個方法的時候系統去方法列表裏查找這個方法,找到了就執行
所以,調用壹個方法就會進行壹次發送消息也就是在這個類的方法列表裏找,如果在該類中找不到就到該類的父類裏找,如果父類還找不到就壹直搜索到繼承樹的根部,如果找不到或者消息轉發不成功那就會報unrecognized selector錯。
1.動態方法解析
Objective-C運行時會調用+resolveInstanceMethod:或者?+resolveClassMethod:讓妳有機會提供壹個函數實現,如果妳添加了函數並且返回YES,那麽運行時就會重新啟動壹次消息發送的過程。如下圖:
雖然沒有foo:的實現函數,但是通過class_addMethod()動態添加了fooMethod函數,並執行了這個函數並且打印成功。如果reslove返回NO運行時就會移到下壹步:forwardingTargetSelector
2.直接消息轉發
如果目標對象實現了forwardingTargetSelector,Runtime這時就會調用這個方法,給妳把這個消息轉發給其他對象的機會
從圖中可以看出我們通過forwardingTargetForSelector方法將當前類的方法轉給Father類實現了,打印成功。
3.完整消息轉發
如果在上壹步還不能處理未知消息,那唯壹能做的就是啟動消息轉發機制。首先它會發送methodSignatureForSelector消息獲得函數的參數和返回值類型。如果methodSignatureForSelector返回nil,Runtime則會發出doesNotRecognizeSelector。如果返回壹個簽名函數,Runtime就會創建壹個NSInvocation對象並發送forwardInvocation消息給目標對象。
Runtime的實際應用
1.使用Runtime交換方法
2.動態添加方法(目前不是很懂)
3.給分類添加屬性
4.消息轉發(熱更新)解決Bug(JSPatch)