運行時(runtime)系統(tǒng)是一個提供一系列公開函數(shù)接口以及數(shù)據(jù)結(jié)構(gòu)的動態(tài)鏈接庫
消息傳遞機制:
OC是動態(tài)語言,所有方法的調(diào)用都會通過runtime動態(tài)地轉(zhuǎn)換成消息發(fā)送,這個過程中就用到了相應(yīng)的運行時方法:
(1) 在傳遞給接受者(實例對象)的過程中,需要通過對象的isa指針尋找“對象所屬的類對象”,在這之前,引入了“OC對象”的內(nèi)存布局:
OC對象包含一個isa指針,指向其所屬的類對象,同時包含其所有父類的實例變量。
(2) 接著,在其父類對象的方法列表中尋找方法以供調(diào)用,這里又引出了“類對象”中存放的內(nèi)容:
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?<1> ?實例對象的方法列表(方法名稱,IMP實現(xiàn),參數(shù)類型)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?<2> ?成員變量列表
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?<3> ?屬性列表
(3) 然后,因為類對象也是對象,所有也有isa指針,指向元對象。引入“元類對象”中存放的內(nèi)容:
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? <1>? 類方法列表 (方法名稱,IMP實現(xiàn),參數(shù)類型)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? <2> ?superclass指針
(4) 實例對象,類對象,元對象通過isa指針關(guān)聯(lián),類對象和元對象通過superclass指針與父類關(guān)聯(lián),從而形成如下圖一樣的閉環(huán):
PS: ??<1> 根類是NSObject,它的superclass指針指向nil。
? ? ? ? ? <2> 所有元類對象的isa指針都指向根元類,根元類 ?isa指針指向自己,superclass指針指向NSObject。
(5) 接著(2)中,找完父類的方法列表,沒找到,會繼續(xù)找父類的父類,一直找到頂層的父類,如果沒有找到會拋出“unrecognized selector sent to XXX”異常。在這之前,runtime提供了三次拯救程序崩潰的機會:
? ? ? ? ? ?<1> Method resolution
? ? ? 你可以通過實現(xiàn) +resolveInstanceMethod: 以及?+resolveClassMethod: 方法以動態(tài)的實現(xiàn)一個指定實例的實例方法或者類方法。
PS: 一般說來,消息轉(zhuǎn)發(fā)機制?和?動態(tài)加載機制?是正交的。在轉(zhuǎn)發(fā)機制介入之前,一個類有機會動態(tài)的加載一個方法。
? ? ? ? ? ? <2> Fast forwarding
? ? ? ?如果對象實現(xiàn) -forwardingTargetForSelector:,runtime就會調(diào)用這個方法,給把這個消息轉(zhuǎn)發(fā)給其他對象的機會。
? ? ? ? ? ? <3> Normal forwarding
? ? ? ? 運行時發(fā)送 -methodSignatureForSelector: 消息獲得函數(shù)參數(shù)和返回值類型,如果返回nil,運行時會發(fā)送 -doesNotRecognizeSelector消息,然后拋出(5)中異常。如果返回了函數(shù)簽名,運行時就會創(chuàng)建一個NSInvocation對象并發(fā)送 -forwardInvocation: 消息給目標對象。
PS:
? ? ? ? 為了加速上述消息調(diào)用的過程。運行時系統(tǒng)會緩存它們使用過的方法名以及對應(yīng)的方法地址。這些緩存是針對每個類單獨緩存的,當然,這其中包含它自己定義的方法以及繼承自父類的方法。在搜索方法列表之前,消息傳遞路由會先檢查接收消息接收類的緩存(基于如果干過一次就很可能還會干下一次的理論)。如果方法名存在緩存中,消息傳遞只比直接的函數(shù)調(diào)用慢那么一點點。一旦一個程序已經(jīng)跑了足夠長的時間以“預(yù)熱”他的緩存,幾乎所有發(fā)送的消息就都可能被緩存了。緩存是根據(jù)程序運行時新消息的調(diào)用動態(tài)增長的。
引文:
? ? ? ??OC運行時編程指南
? ? ??《招聘一個靠譜的iOS》面試題參考答案