oc是一個全動態語言,oc的一切都是基於runtime實現的!
從以下三方面來理解runtime吧!
1. 傳統的面向過程的語言開發,例如c語言。實現c語言編譯器很簡單,只要按照語法規則實現一個LALR語法分析器就可以了,編譯器優化是非常難的topic,不在這里討論范圍內,忽略。 這里我們實現了編譯器其中最最基礎和原始的目標之一就是把一份代碼里的函數名稱,轉化成一個相對內存地址,把調用這個函數的語句轉換成一個jmp跳轉指令。在程序開始運行時候,調用語句可以正確跳轉到對應的函數地址。 這樣很好,也很直白,但是。。。太死板了。everything is per-determined
2. 我們希望靈活,於是需要開發面向對象的語言,例如c++。 c++在c的基礎上增加了類的部分。但這到底意味着什么呢?我們在寫它的編譯器要如何考慮呢?其實,就是讓編譯器多繞個彎,在嚴格的c編譯器上增加一層類處理的機制,把一個函數限制在它處在的class環境里,每次請求一個函數調用,先找到它的對象, 其類型,返回值,參數等等,確定了這些后再jmp跳轉到需要的函數。這樣很多程序增加了靈活性同樣一個函數調用會根據請求參數和類的環境返回完全不同的結果。增加類機制后,就模擬了現實世界的抽象模式,不同的對象有不同的屬性和方法。同樣的方法,不同的類有不同的行為! 這里大家就可以看到作為一個編譯器開發者都做了哪些進一步的思考。但是。。。還是死板, 我們仍然叫c++是static language。
3. 希望更加靈活! 於是我們完全把上面哪個類的實現部分抽象出來,做成一套完整運行階段的檢測環境。這次再寫編譯器甚至保留部分代碼里的sytax名稱,名稱錯誤檢測,runtime環境注冊所有全局的類,函數,變量等等信息等等,我們可以無限的為這個層增加必要的功能。調用函數時候,會先從這個運行時環境里檢測所以可能的參數再做jmp跳轉,這就是runtime。編譯器開發起來比上面更加彎彎繞。但是這個層極大增加了程序的靈活性。 例如當調用一個函數時候,前2種語言,很有可能一個jmp到了一個非法地址導致程序crash, 但是在這個層次里面,runtime就過濾掉了這些可能性。 這就是為什么dynamic langauge更加強壯。 因為編譯器和runtime環境開發人員已經幫你處理了這些問題。
好了上面說着這么多,我們再返回來看objective-c. 現在你是不是能理解這樣的語句了呢?
id obj=self;
if ([obj respondsToSelector:@selector(function1:)) {
}
if ([obj isKindOfClass:[NSArray class]] ) {
}
if ([obj conformsToProtocol:@protocol(myProtocol)]) {
}
if ([[obj class] isSubclassOfClass:[NSArray class]]) {
}
[obj someNonExistFunction];
看似很簡單的語句,但是為了讓語言實現這個能力,語言開發者要付出很多努力實現runtime環境。這里運行時環境處理了弱類型、函數存在檢查工作。runtime會檢測注冊列表里是否存在對應的函數,類型是否正確,最后確定下來正確的函數地址,再進行保存寄存器狀態,壓棧,函數調用等等實際的操作。
id knife=[Knife grateKnife];
NSArray *monsterList=[NSArray array];
[monsterList makeObjectsPerformSelector:@selector(killMonster:) withObject:knife];
在c,c++年代去完成這個功能是非常麻煩的,但是動態語言卻非常簡單。
關於執行效率問題。 “靜態語言執行效率要比動態語言高”,這句沒錯。因為一部分cpu計算損耗在了runtime過程中。而靜態語言生成的機器指令更簡潔。正因為知道這個原因,所以開發語言的人付出很大一部分努力為了保持runtime小巧上。所以objecitve-c是c的超集+一個小巧的runtime環境。 但是,換句話說,從算法角度考慮,這點復雜度不算差別的,Big O notation結果不會有差別。( It's not log(n) vs n^2 )
簡單理解:“Runtime is everything between your each function call.”
Runtime好比objective-c的靈魂。很多東西都是在這個基礎上出現的。所以它是指的你花功夫去理解的。
