張國文
摘 要:通過筆者多年i0S應(yīng)用開發(fā)的經(jīng)驗,就i0S應(yīng)用開發(fā)的運行時機制做一個淺析,從運行時機制是什么,到運行時機制的基本原理,與運行時交互的方式,及運行時中的數(shù)據(jù)結(jié)構(gòu),為希望從事iOS開發(fā)的朋友打開一扇門。
關(guān)鍵詞:RunTime;運行時機制;動態(tài)調(diào)用
RunTime簡稱運行時。OC就是運行時機制,也就是在運行時候的一些機制,其中最主要的是消息機制。對于C語言,函數(shù)的調(diào)用在編譯的時候會決定調(diào)用哪個函數(shù)。對于OC的函數(shù),屬于動態(tài)調(diào)用過程,在編譯的時候并不能決定真正調(diào)用哪個函數(shù),只有在真正運行的時候才會根據(jù)函數(shù)的名稱找到對應(yīng)的函數(shù)來調(diào)用。
1 怎么理解OC是動態(tài)語言,Runtime又是什么?
靜態(tài)語言:如C語言,編譯階段就要決定調(diào)用哪個函數(shù),如果函數(shù)未實現(xiàn)就會編譯報錯。
動態(tài)語言:如OC語言,編譯階段并不能決定真正調(diào)用哪個函數(shù),只要函數(shù)聲明過即使沒有實現(xiàn)也不會報錯。
我們常說OC是一門動態(tài)語言,就是因為它總是把一些決定性的工作從編譯階段推遲到運行時階段。OC代碼的運行不僅需要編譯器,還需要運行時系統(tǒng)(Runtime Sytem)來執(zhí)行編譯后的代碼。
Runtime是一套底層純C語言API,OC代碼最終都會被編譯器轉(zhuǎn)化為運行時代碼,通過消息機制決定函數(shù)調(diào)用方式,這也是OC作為動態(tài)語言使用的基礎(chǔ)。
2 理解消息機制的基本原理
OC的方法調(diào)用都是類似[receiver selector]的形式,其實每次都是一個運行時消息發(fā)送過程。
第一步:編譯階段
[receiver selector]方法被編譯器轉(zhuǎn)化,分為兩種情況:
(1)不帶參數(shù)的方法被編譯為:objc_msgSend(receiver,selector)
(2)帶參數(shù)的方法被編譯為:objc_msgSend(recevier,selector,org1,org2,…)
第二步:運行時階段
消息接收者recever尋找對應(yīng)的selector,也分為兩種情況:
(1)接收者能找到對應(yīng)的selector,直接執(zhí)行接收receiver對象的selector方法。
(2)接收者找不到對應(yīng)的selector,消息被轉(zhuǎn)發(fā)或者臨時向接收者添加這個selector對應(yīng)的實現(xiàn)內(nèi)容,否則崩潰。
3 與Runtime的交互
Runtime的官方文檔中將OC與Runtime的交互劃分三種層次:OC源代碼,NSObject方法,Runtime 函數(shù)。這其實也是按照與Runtime交互程度從低到高排序的三種方式。
3.1 OC源代碼(Objec-C Source Code)
我們已經(jīng)說過,OC代碼會在編譯階段被編譯器轉(zhuǎn)化。OC中的類、方法和協(xié)議等在Runtime中都由一些數(shù)據(jù)結(jié)構(gòu)來定義。所以,我們平時直接使用OC編寫代碼,其實這已經(jīng)是在和Runtime進行交互了,只不過這個過程對于我們來說是無感的。
3.2 NSObject方法(NSObject Methods)
Runtime的最大特征就是實現(xiàn)了OC語言的動態(tài)特性。作為大部分Objective-C類繼承體系的根類的NSObject,其本身就具有了一些非常具有運行時動態(tài)特性的方法,比如respondsToSelector:方法可以檢查在代碼運行階段當前對象是否能響應(yīng)指定的消息,所以使用這些方法也算是一種與Runtme的交互方式。
3.3 使用Runtime函數(shù)(Runtime Functions)
Runtime系統(tǒng)是一個由一系列函數(shù)和數(shù)據(jù)結(jié)構(gòu)組成,具有公共接口的動態(tài)共享庫。頭文件存放于/usr/include/objc目錄下。在我們工程代碼里引用Runtime的頭文件,同樣能夠?qū)崿F(xiàn)類似OC代碼的效果。
4 深入理解Rutime消息發(fā)送
我們在分析了OC語言對應(yīng)的底層C結(jié)構(gòu)之后,現(xiàn)在可以進一步理解運行時的消息發(fā)送機制。先前講到,OC調(diào)用方法被編譯轉(zhuǎn)化為如下的形式:
id _Nullable objc_msgSend(id _Nullable self,SEL _Nonnull op,…)
其實,除了常見的objc_msgSend,消息發(fā)送的方法還有objc_msgSend_stret,objc_msgSendSuper,objc_msgSendSuper_stret等,如果消息傳遞給超類就使用帶有super的方法,如果返回值是結(jié)構(gòu)體而不是簡單值就使用帶有stret的值。
5 最后總結(jié)
通過iOS的runtime機制,可以更加靈活的進行開發(fā)和調(diào)試。
參考文獻
[1]Matt Galloway [Effective Objective-C 2.0] 2014.
[2]Vandad Nahavandipoor [iOS 7 Programming Cookbook]2013.