詳解Objective-C runtime

原文地址:http://blog.securemacprogramming.com/2013/12/by-your-_cmd/php

 

感謝翻譯小組成員wingpan熱心翻譯。本篇文章是咱們每週推薦優秀國外的技術類文章的其中一篇。若是您有不錯的原創或譯文,歡迎提交給咱們,更歡迎其餘朋友加入咱們的翻譯小組(聯繫qq:2408167315)。html


本文是我在 Alt Tech Talks: London 上關於 Objective-C runtime的演講總結,若是你對Objective-C runtime感興趣的話,應該看看這篇文章,特別是文章中的連接,必定會受益不淺。 ios

 

什麼是Objective-C runtime?程序員

簡單來講,Objective-C runtime是一個實現Objective-C語言的C庫。對象能夠用C語言中的結構體表示,而方法(methods)能夠用C函數實現。事實上,他們 差很少也是這麼幹了,另外再加上了一些額外的特性。這些結構體和函數被runtime函數封裝後,Objective-C程序員能夠在程序運行時建立,檢 查,修改類,對象和它們的方法。編程

 

 除了封裝,Objective-C runtime庫也負責找出方法的最終執行代碼。當程序執行[object doSomething]時,不會直接找到方法並調用。相反,一條消息(message)會發送給對象(在這兒,咱們一般叫它接收者)。runtime庫 給次機會讓對象根據消息決定該做出什麼樣的反應。Alan Kay反覆強調消息傳遞(message-passing)是Smalltalk最重要的部分(Objective-C根據Smalltalk發展而來),而不是對象: 數組

 

因爲之前關於這個話題我創造了「對象」這個詞,如今不少人都對這個概念趨之若鶩,這讓我感到很是遺憾。緩存

 

其實這裏面更爲重要的理念是「消息命令」(messaging),這纔是Smalltalk的核心內容(如今尚有一些內容尚未所有完成)。日 語中有個簡短的單詞叫作「ma」,它用來表示兩個物體之間的東西,在英語中和它最相近的單詞也許是「interstitial」。製造一個龐大且可擴展系 統的關鍵是設計它各個模塊之間的通訊方式,而不是關注它的內部屬性和行爲。 app

 

 

實際上,在一篇介紹Smalltalk虛擬機的文章裏,這門編程技術被叫作消息傳遞或者消息傳送範式。「面向對象」一般用來描述內存管理系統。異步

 

在演講和文章中都使用ObjC runtime這個詞,看似只有一個,實際上存在不少runtime庫。雖然它們都支持對象的自省檢查和消息接收,可是它們卻有不一樣的特性和實現方式(例 如,一樣是發送消息,Apple的runtime用一步完成,而GNU runtime會先查詢這些消息,而後執行查找到的函數分兩步完成)。如下全部的討論,都是基於Apple的最新runtime庫(蘋果公司在OSX 10.5和iOS發佈時的版本)。 ide

 

在那次演講中,我決定研究runtime庫某些領域的功能。我找了一些但願更透徹瞭解的東西,而後把它們作成問答的形式組成個人演講。

 

動態建立類

 

如何實現Key-Value Observing?

 當我在準備此次演講時,一篇叫作KVO considered harmful 的文章開始擁有不少擁躉。它提出了不少對KVO正確的批評,但相對於捨棄觀察者模式不用,我更想探索出一種新的實現方式。 

 

KVO實現觀察者模式的關鍵是它偷偷摸摸將被觀察對象的類改變了,它子類化原來的類後,就可以自定義該對象的方法來調用KVO的回調方法。這些都是經過 objc_duplicateClass這個方法完成,但很遺憾,這個方法並不公開,咱們沒法私自調用。 

 

條條大路通羅馬,好在除了objc_duplicateClass,還有其餘方法能夠經過使用祕密子類化的方式實現觀察者模式,好比建立和註冊 「class pair」。那麼什麼是class pair呢?對於Objective-C的類來講,都有一對Class的對象來定義它:Class對象定義了這個類的實例方法,而metaclass定義 了這個類的類方法。因此每一個class實際上是它metaclass的單例。 

 

這個代碼展 示了觀察者模式的工做原理。當你給對象增長觀察者時,這個對象首先會檢查本身是否可被觀察,若是是,它會新建立一個類,用咱們本身的-dealloc替代 原來類的方法,一樣它也會把-class方法替換掉,相似於KVO被觀察對象,當你訪問被觀察對象的類名時,返回的是它原來的類名,而不是新生成的類。

 

建立完類後,咱們須要照着 Key-Value Coding爲屬性增長一個setter方法:這個setter方法會獲取這個屬性修改前的值和修改後的值,而後調用block形式的回調函數,將這兩個值告訴觀察者。代碼中根據咱們的意願,這個block能夠異步調用。 

 

請注意, -addObserverForKey:withBlock:會使用s object_setClass() 將被觀察對象的類替代爲新組建的類。這樣作最主要的目的是將消息轉變爲方法的方式改變,可是這須要很是當心,原來的類和新的類必須有相同的成員變量佈局。 由於成員變量也是用過runtime訪問,修改某個對象的類可能致使runtime沒法找到對應的變量。 

 

咱們在存儲觀察者集合時遇到些麻煩,由於沒地方去存它們。給ObserverPattern這個類增長成員變量不起做用,由於根本沒有生成這個類的對象。被觀察對象的成員變量是它原來類的,它並無考慮過這些觀察者。 

 

Objective-C runtime經過引入 associated objects 幫助咱們擺脫這個困境。在runtime裏,理論上全部對象均可以擁有包含其餘對象的字典。經過associated references,被觀察對象能夠存儲和訪問他們的觀察者,而不須要額外的成員變量。

 

若是你運行屢次後,你會發現ObserverPattern 仍是有點小毛病的。因爲觀察者回調是異步調用的,觀察者接

 

收到的變化事件也是亂序的。這意味着觀察者其實沒法區分被觀察屬性的最終狀態是什麼,回調中的新值可能早已被修改。我這樣作的目的是爲了說明在KVO中同步調用回調實際上是個有用的特點,並不是bug。 


建立對象

 

那些額外的字節都是幹啥用的?

當你建立一個 Objective-C對象時,runtime會在實例變量存儲區域後面再分配一點額外的空間。這麼作的目的是什麼呢?你能夠獲取這塊空間起始指針(用 object_getIndexedIvars),而後就能夠索引實例變量(ivars)。好吧,下面我會使用自定義數組來講明一下索引ivars的用 處。 

 

讓咱們建立一個數組!從這個SimpleArray中能夠看到兩件事情:最明顯的一件是它使用了類簇模式。 當使用+alloc方法返回對象時,通常狀況下已經爲這個對象分配了全部的內存,可是在這個例子中,在+alloc時並不知道須要多大的內存空間。只有當 調用了 -initWithObjects:count:之後,才能根據數組內對象數量計算出這個數組須要多大的內存,因此+alloc只是返回一個佔位符,只有 在初始化後纔會分配和返回真正的數組對象。 

 

或許你會問爲何咱們要用類簇把事情搞那麼複雜,使用 calloc()另外分配一塊大小合適的緩存,而後把那些對象指針存到裏面不就得了?答案是但願利用局部性原理提升訪問性能。從數組的設計上咱們能夠看出,每次數組指針被訪問時,以後會有很大概率訪問到緩存指針,因此把它們肩並肩的放入內存意味着找到其中一個就是找到了另一個。 

 

消息派發

 

消息如何轉發?

Objective-C其中一個強大特性是對象不須要實現某個方法,儘管它在編譯時聲明瞭該選擇符(selector)。但它能夠在運行時再決 定方法實現,或者將這些消息轉發給其餘對象,或者發出異常,亦或作一些其餘事情。可是這個特性的某些方面曾經一直困擾我:消息轉發(message forwarding)會調用 -forwardInvocation:,而後傳入一個NSInvocation 對象。可是這個NSInvocation 類是在Foundation庫中定義的,難道說runtime工做須要Foundation配合? 

 

我試着挖掘其中的緣由,發現答案並非我想的那樣,runtime不須要知道Foundation。runtime會讓程序定義轉發函數 (forwarding function),當 objc_msgSend()沒法找到該selector的實現時,那個轉發函數就會被調用。程序一啓動,CoreFoundation就將 -forwardInvocation:定義成轉發函數。 

 

讓咱們來建立一個Ruby! 固然並非真的實現完整的Ruby,Ruby有一個叫作#method_missing的函數,當對象收到一個它沒有實現的消息時,這個函數就會被調到, 這和Smalltalk的作法比較類似。使用objc_setForwardHandler,咱們也能在Objective-C的類中實現相似Ruby的 methodMissing:方法。 

 

總結

Objective-C runtime能夠有效的幫助咱們爲程序增長不少動態的行爲。一些開發者除了使用method swizzling幫助調試程序,並不會在實際程序中使用它,但runtime編程的確有不少功能,它應該成爲實際應用代碼編寫的重要工具。

相關文章
相關標籤/搜索