剛接觸runtime的時候,就感覺很陌生,完全沒有見過相關(guān)代碼归榕。所以趁著閑時間看了看官方解釋以及網(wǎng)上各種大佬的詳細分析,用自己的大白話總結(jié)一下吱涉,不足之處還希望各位大佬們指點一下刹泄,共同進步嘛!
1. Runtime是什么怎爵。
在這里先放上runtime的源碼和runtime官方api:
*源碼:objc-runtime
*官方API:Objective-C Runtime
Objective-C 擴展了 C 語言循签,并加入了面向?qū)ο筇匦院?Smalltalk 式的消息傳遞機制。而這個擴展的核心是一個用 C 和 編譯語言 寫的 Runtime 庫疙咸。它是 Objective-C 面向?qū)ο蠛蛣討B(tài)機制的基石。Objective-C 是一個動態(tài)語言风科,這意味著它不僅需要一個編譯器撒轮,也需要一個運行時系統(tǒng)來動態(tài)得創(chuàng)建類和對象乞旦、進行消息傳遞和轉(zhuǎn)發(fā)。理解 Objective-C 的 Runtime 機制可以幫我們更好的了解這個語言题山,適當?shù)臅r候還能對語言進行擴展兰粉,從系統(tǒng)層面解決項目中的一些設計或技術(shù)問題。了解 Runtime 顶瞳,要先了解它的核心 - 消息傳遞 (Messaging)玖姑。
對于從事iOS開發(fā)的人來說,面試差不多都會問慨菱。所有人都會回答【Runtime是運行時】焰络,但是再往下具體的有深度的對于我來說就只能說個皮毛了。無非就是字典轉(zhuǎn)模型符喝,歸檔/解檔闪彼,給分類動態(tài)添加屬性,交換方法协饲。畏腕。。廢話就到這了茉稠!回到正題描馅。
由上面一長段話可以得出,Runtime的核心就是消息傳遞而线。
下面來看看所謂的運行時都干了什么铭污!
一個對象的方法例如這樣[obj doSomething],到了編譯器轉(zhuǎn)成消息發(fā)送objc_msgSend(obj, @selector(doSomething));如果帶參數(shù)的話吞获,如:[obj doSomething:(id)arg...];到了編譯器轉(zhuǎn)成消息發(fā)送objc_msgSend(obj, selector, arg1, arg2, ...);
objc_msgSend就是消息傳遞况凉。在了解objc_msgSend之前,必須要知道以下兩點:
1.1 類的定義
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY;
//isa指針. 對象的指針—>類各拷,類的指針—>元類刁绒,元類—>根元類—>自己 形成閉環(huán);
#if !__OBJC2__
Class super_class;//父類
const char *name;//類名
long version;//類的版本信息烤黍,默認為0
long info;//類信息知市,供運行期使用的一些位標識
long instance_size;//類的實例變量大小
struct objc_ivar_list *ivars;// 類的成員變量鏈表
struct objc_method_list **methodLists;// 方法鏈表
struct objc_cache *cache;//方法緩存
struct objc_protocol_list *protocols;//協(xié)議鏈表#
endif
}OBJC2_UNAVAILABLE;
1.2 objc_method_list里面有什么?
typedef struct objc_method * Method;
struct objc_method
{
SEL method_name;//方法名稱
charchar * method_typesE;//參數(shù)和返回類型的描述
IMP method_imp;//方法具體實現(xiàn)的指針速蕊,指向?qū)膶崿F(xiàn)代碼
}
SEL:
從SEL類型的成員為method_name可以知道嫂丙,SEL大概代表一個方法的名字,作用是標記方法區(qū)分方法
IMP:
IMP是一個函數(shù)指針规哲,指向objc_method對應方法的實現(xiàn)部分跟啤。
下面我們就說說Runtime的核心objc_msgSend消息傳遞。
2 消息傳遞的流程
1.objc_msgSend()函數(shù)會根據(jù)調(diào)用的對象isa指針找到所屬的class中的objc_method_list。然后從上向下遍歷隅肥,根據(jù)SEL的方法名稱竿奏,找到IMP指針跳轉(zhuǎn)到方法的實現(xiàn)代碼,調(diào)用這個方法的實現(xiàn)腥放。
2.如果找不到泛啸,會根據(jù)所屬類的superClass指針,沿著類的繼承體系繼續(xù)向上查找(向父類查找)秃症,如果 能找到與名稱相符的方法候址,就根據(jù)IMP指針跳轉(zhuǎn)到方法的實現(xiàn)代碼,調(diào)用這個方法的實現(xiàn)种柑。
3.如果在繼承體系中還是找不到相符的方法岗仑,此時就會執(zhí)行”消息轉(zhuǎn)發(fā)(message forwarding)“操作。
3 消息轉(zhuǎn)發(fā)機制
消息轉(zhuǎn)發(fā)機制包括三類:1.類的動態(tài)方法解析2.備用接受者對象3.完整的消息轉(zhuǎn)發(fā)
1.類的動態(tài)方法解析:征詢消息接受者所屬的類莹规,看其是否能動態(tài)添加方法赔蒲,以處理當前“這個未知的選擇子(unknown selector)。
實例對象在接受到無法解讀的消息后良漱,首先會調(diào)用其所屬類的下列類方法:
+ (BOOL)resolveInstanceMethod:(SEL)selector
類對象在接受到無法解讀的消息后舞虱,那么運行期系統(tǒng)就會調(diào)用另外的一個方法:
+ (BOOL)resolveClassMethod:(SEL)selector
如果運行期系統(tǒng)已經(jīng)執(zhí)行完了動態(tài)方法解析,那么消息接受者自己就無法再以動態(tài)新增方法的形式來響應包含該未知選擇子的消息了母市,此時就進入了第二階段——完整的消息轉(zhuǎn)發(fā)矾兜。運行期系統(tǒng)會請求消息接受者以其他手段來處理與消息相關(guān)的方法調(diào)用。
2.備用接受者對象(replacement receiver)
當前接受者如果不能處理這條消息患久,運行期系統(tǒng)會請求當前接受者讓其他接受者處理這條消息椅寺,與之對應的方法是:
- (id)forwardingTargetForSelector:(SEL)selector
方法參數(shù)代表未知的選擇子,返回值為備援接受者蒋失,若當前接受者能找到備援接受者返帕,就直接返回,這個未知的選擇子將會交由備援接受者處理篙挽。如果找不到備援接受者荆萤,就返回nil,此時就會啟用”完整的消息轉(zhuǎn)發(fā)機制“铣卡。
3.完整的消息轉(zhuǎn)發(fā)
如果轉(zhuǎn)發(fā)算法已經(jīng)來到了這一步链韭,那么代表之前的所有轉(zhuǎn)發(fā)嘗試都失敗了,此時只能啟用完整的消息轉(zhuǎn)發(fā)機制煮落。
完整的消息轉(zhuǎn)發(fā)機制是這樣的:首先創(chuàng)建NSInvocation對象敞峭,把尚未處理的那條消息有關(guān)的全部細節(jié)封裝于這個NSInvocation對象中。
此對象中包含選擇子(selector)蝉仇、目標(target)及參數(shù)旋讹。在觸發(fā)NSInvocation對象時殖蚕,”消息派發(fā)系統(tǒng)(message-dispatch system)“將親自觸發(fā),把消息派發(fā)給目標對象骗村。此步驟中會調(diào)用下面這個方法來轉(zhuǎn)發(fā)消息:
- (void)forwardInvocation:(NSInvocation *)invocation
調(diào)用這個方法如果不能處理就會調(diào)用父類的相關(guān)方法嫌褪,一直到NSObject的這個方法,如果NSObject都無法處理就會調(diào)用doesNotRecognizeSelector:方法拋出異常胚股。
以上就是我對Runtime的理解。希望對有緣人有幫助裙秋。如果有不足或錯誤的地方琅拌,還望各位好友指出,共同進步摘刑。