標籤:
// ---------------------------------------------------
參考:南峰子的技術部落格 http://southpeak.github.io
//----------------------------------------------------
OC語言是一門動態語言,它將很多靜態語言在編譯和連結時期做的事放到了運行時來處理。這種動態語言的優勢在於:我們編寫代碼時更具靈活性,如我們可以把訊息轉寄給我們想要的對象,或者隨意交換一個方法的實現等。
這種特性意味著OC不僅需要一個編譯器,還需要一個運行時系統來執行編譯的代碼,對於OC來說,這個運行時系統就像一個作業系統一樣,它讓所有的工作可以正常的運行。這個運行時系統即Objc Runtime。 Objc Runtime其實是一個Runtime庫,它基本上是用C喝彙編寫的,這個庫使得C語言有了物件導向的能力
Runtime庫主要做下面幾件事:
1.封裝:在這個庫中,對象可以用C語言中的結構體表示,而方法可以用C函數來實現,另外再加上了一些額外的特性。這些結構體和函數被runtime函數封裝後,我們就可以在程式運行時建立、檢查、修改類、對象和它們的方法了。
2.找出方法的最終執行代碼:當程式執行[object doSomething]時,會向訊息接收者(object)發送一條訊息(doSomething),runtime會根據訊息接收者是否能響應該訊息而做出不同的反應。這將在後面詳細介紹。
OC runtime目前有兩個版本:Modern runtime和Legacy runtime。Modern Runtime覆蓋了64位的Mac OS X Apps,還有iOS Apps,Legacy Runtime 是早起用來給32位Mac OS X Apps用的。
runtime的基本工作原理,以及如何利用它讓程式編的更加靈活。
類與對象基礎資料結構
Class
OC中,類是由Class 類型來表示的,它實際上是一個指向objc_class結構體的指標。定義如下:
typedef struct objc_claa *Class ;
查看objc/runtime.h中objc_class結構體的定義如下:
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE; // 父類
const char *name OBJC2_UNAVAILABLE; // 類名
long version OBJC2_UNAVAILABLE; // 類的版本資訊,預設為0
long info OBJC2_UNAVAILABLE; // 類資訊,供運行期使用的一些位標識
long instance_size OBJC2_UNAVAILABLE; // 該類的執行個體變數大小
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE; // 該類的成員變數鏈表
struct objc_method_list **methodLists OBJC2_UNAVAILABLE; // 方法定義鏈表
struct objc_cache *cache OBJC2_UNAVAILABLE; // 方法緩衝
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; // 協議鏈表
#endif
}OBJC2_UNAVAILABLE;
在這個定義中,下面幾個欄位是我們感興趣的
1.isa: 需要注意的是,在OC中,所有的類自身也是一個對象,這個對象的Class裡面也有一個isa指標,它指向metaClass(元類),後面詳解
2.super class: 指向該類的父類,如果該類已經是最頂層的根類(如NSObject或NSProxy),則super_class為NULL.
3.cache: 用於緩衝最近使用的方法. 一個接收者對象收到一個訊息時,它會根據isa指標去尋找能夠響應這個訊息的對象,在實際使用中,這個對象只有一部分方法是常用的,很多方法其實很少用或者根本用不上,這種情況下,如果每次訊息來,我們都到methodLists中遍曆一遍,效能勢必很差,這時,cache就派上用場了,在我們每次調用過一個方法後,這個方法就會被緩衝到cache列表中,下次調用的時候,runtime會優先去cache中尋找,如果cache沒有,採取methodLists中尋找方法.這樣,對於那些經常用到的方法的調用,便提高了調用的效率
4.version: 我們可以使用這個欄位來提供類的版本資訊,這對於類的序列化非常有用,它可以讓我們識別出不同類定義版本中執行個體變數布局的改變,針對cache,下面例子來說明執行過程"
NSArray *array = [[NSArray alloc] init];
流程 :
1.[NSArray alloc]執行, 因為NSArray 沒有+alloc方法,於是去父類NSObject尋找
2.檢測NSObject是否響應 + alloc方法,發現響應, 於是檢測NSArray類,並根據其所需的記憶體空間開始分配記憶體,然後把isa指標指向NSArray類.同時,+alloc也被加進cache列表裡
3.接著,執行-init方法,如果NSArray回應程式法,則直接將其加入cache;如果不響應,則取父類尋找.
4.在後期的操作中,如果再以[[NSArray alloc] init] 方式建立數組,則會直接從cache中取出相應的方法,直接調用
objc_object與id
objc_object是表示一個類的執行個體結構體,它的定義如下(objc/objc.h)
struct objc_object {
Class isa OBJC_ISA_AVAILABILITY;
};
typedef struct objc_objct *id;
可以看到,這個結構體只有一個字,即指向其類的isa指標,這樣,當我們向一個OC對象發送訊息時,執行階段程式庫會根據執行個體對象的isa指標找到這個執行個體對象所屬的類.Runtime庫會在類的方法列表及父類的方法列表中尋找與訊息對應的selector指向的方法.找到後即運行這個方法.
當建立一個特定類的執行個體對象時,分配的記憶體包含一個objc_object資料結構,然後是累的額執行個體變數的資料.NSObject類的alloc和allocWithZone:方法使用函數class_createInstance來建立objc_object資料結構.
另外還有我們常見的id,它是一個objc_object結構類型的指標.它的存在可以讓我們實作類別似於C++眾泛型的一些操作,該類型的對象可以轉換為任何一種對象,有點類似於C語言中void*指標類型的作用.
iOS Objective -C Runtime 運行時之一: 類與對象