Objective-C Runtime (一):類與對象

Objective-C Runtime (一):類與對象

Runtime介紹html

Objective-C語言是一門動態語言,它將不少靜態語言在編譯和連接時期作的事放到了運行時來處理。這意味着它不只須要一個編譯器,也須要一個運行時系統來動態得建立類和對象、進行消息傳遞和轉發。這個運行時系統即Objc Runtime。Objc Runtime實際上是一個Runtime庫,它基本上是用C和彙編寫的,這個庫使得C語言有了面向對象的能力。數組

Runtime庫主要作下面幾件事:緩存

  1. 封裝:在這個庫中,對象能夠用C語言中的結構體表示,而方法能夠用C函數 來實現,另外再加上了一些額外的特性。這些結構體和函數被runtime函數封裝後,咱們就能夠在程序運行時建立,檢查,修改類、對象和它們的方法了。
  2. 消息傳遞:當程序執行[object doSomething]時,會向消息接收者(object)發送一條消息(doSomething),runtime會根據消息接收者是否能響應該消息而作出不一樣的反應。這將在後面詳細介紹。

在接下來的一系列文章中,我將介紹一下runtime的基本工做原理以及在咱們的代碼裏能夠用它來作什麼。在本文中,咱們先來了解一下類與對象。bash

類與對象基礎數據結構

###OC對象 OC的對象主要能夠分爲三種:數據結構

  1. instance對象(實例對象)
  2. class對象(類對象)
  3. meta-class對象(元類對象):主要存儲着isa指針,superclass指針,類的類方法的信息(class method)。

instance對象 它主要是經過類alloc出來的對象,每次調用alloc都會產生新的instance對象。app

NSObjcet *object1 = [[NSObjcet alloc] init];
複製代碼

instance對象在內存中存儲的信息包括:ide

  1. isa指針
  2. 成員變量

class對象 咱們經過class方法或runtime方法獲得一個class對象。class對象也就是類對象。==每個類在內存中有且只有一個class對象==。函數

Class objectClass1 = [NSObject class];
// runtime
Class objectClass2 = object_getClass(object1);
複製代碼

class對象在內存中存儲的信息主要包括:性能

  1. isa指針
  2. superclass指針
  3. 類的屬性信息(@property)
  4. 類的成員變量信息(ivar)
  5. 類的對象方法信息(instance method)
  6. 類的協議信息(protocol)

meta-class對象 咱們經過runtime方法獲得一個class對象。==每一個類在內存中有且只有一個meta-class對象。==ui

//runtime中傳入類對象此時獲得的就是元類對象
Class objectMetaClass = object_getClass([NSObject class]);
// 判斷該對象是否爲元類對象
class_isMetaClass(objectMetaClass) 
複製代碼

meta-class對象在內存中存儲的信息主要包括:

  1. isa指針
  2. superclass指針
  3. 類的類方法的信息(class method)

咱們來看看下面的圖,來分析這三者的關係

從上圖中,咱們能夠分析出:

  1. instance對象的isa指向class
  2. class的isa指向meta-class
  3. meta-class的isa指向基類的meta-class,基類的isa指向本身
  4. class的superclass指向父類的class,若是沒有父類,superclass指針爲nil
  5. meta-class的superclass指向父類的meta-class,基類的meta-class的superclass指向基類的class
  6. instance對象調用對象方法的軌跡,isa找到class,方法不存在,就經過superclass找父類
  7. class調用類方法的軌跡,isa找meta-class,方法不存在,就經過superclass找父類

###Class

Objective-C類是由Class類型來表示的,它其實是一個指向objc_class結構體的指針。在objc/objc.h它的定義以下:

/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;
複製代碼

objc/runtime.h中咱們能夠看到結構體objc_class的定義以下:

struct objc_class {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;

#if !__OBJC2__
    Class _Nullable super_class                              OBJC2_UNAVAILABLE;// 父類
    const char * _Nonnull name                               OBJC2_UNAVAILABLE;// 類名
    long version                                             OBJC2_UNAVAILABLE;// 類的版本信息,默認爲0
    long info                                                OBJC2_UNAVAILABLE;// 類信息,供運行期使用的一些位標識
    long instance_size                                       OBJC2_UNAVAILABLE;// 該類的實例變量大小
    struct objc_ivar_list * _Nullable ivars                  OBJC2_UNAVAILABLE;//該類的成員變量鏈表
    struct objc_method_list * _Nullable * _Nullable methodLists                    OBJC2_UNAVAILABLE;// 方法定義的鏈表
    struct objc_cache * _Nonnull cache                       OBJC2_UNAVAILABLE;// 方法緩存
    struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;// 協議鏈表
#endif

} OBJC2_UNAVAILABLE;
複製代碼

###1. name 類名

// 獲取類的類名
const char * class_getName ( Class cls );
複製代碼

###2. isa 在Objective-C中,類自身也是一個對象,咱們稱之爲類對象,在這個類對象中,也有一個isa指針,它指向了metaClass(元類)

###3. super_class 指向該類的父類,若是該類已是最頂層的根類(如NSObject或NSProxy),則super_class爲nil。

###4. instance_size

// 獲取實例大小
size_t class_getInstanceSize ( Class cls );
複製代碼

###5. objc_ivar_list 成員變量和屬性信息。ivars是一個數組,全部的成員變量、屬性的信息是放在鏈表ivars中的。

######獲取成員變量信息

// 獲取整個成員變量列表
Ivar * class_copyIvarList ( Class cls, unsigned int *outCount );
//獲取一個成員變量的名字
const char * _Nullable ivar_getName(Ivar _Nonnull v) 
//獲取一個成員變量的
const char * _Nullable ivar_getTypeEncoding(Ivar _Nonnull v) 
複製代碼
  • ivar_getTypeEncoding函數獲取的是變量的數據類型,詳細的說明能夠看蘋果官方文檔Type Encodings
  • class_copyIvarList函數,它返回一個指向成員變量信息的數組。這個數組不包含在父類中聲明的變量。outCount指針返回數組的大小。須要注意的是,咱們必須使用free()來釋放這個數組。

######獲取成員變量屬性信息

/// An opaque type that represents an Objective-C declared property.
typedef struct objc_property *objc_property_t;
//獲取整個成員變量屬性列表
objc_property_t _Nonnull *_Nullable class_copyPropertyList(Class _Nullable cls, unsigned int * _Nullable outCount)

/// Defines a property attribute
typedef struct {
    const char * _Nonnull name;           /**< The name of the attribute */
    const char * _Nonnull value;          /**< The value of the attribute (usually empty) */
} objc_property_attribute_t;

objc_property_attribute_t * _Nullable property_copyAttributeList(objc_property_t _Nonnull property,unsigned int * _Nullable outCount)
複製代碼
  • objc_property_attribute_t實際上是對屬性的詳細描述,包括屬性名稱、屬性編碼類型、原子類型/非原子類型等。如T@"NSString",C,N,V_titleTQ,N,V_age

下面詳細地說明property_getAttributes獲取到的屬性的語義:

  • 第一:T老是第一個,它表明類型。 對於類類型,它都是T@類型,其中@表示對象類型;對於id類型,它就是@;而對於基本數據類型,它都是T加上編碼類型(@encode(type)),好比int類型就是Ti。
  • 第二:表示屬性編碼類型,好比是C表示copy,&表示strong,W表示weak等。如果基本類型,它默認是assign,所以此時它是空的。
  • 第三:指定是nonatomic仍是atomic,如果nonatomic,則用N表示,如果atomic,則值爲空。好比的count屬性的詳細描述爲:Ti,N,V_count,它表示int類型、nonatomic、成員變量名爲_count;tomicProperty屬性描述爲:T@NSNumber,&,V_atomicProperty,它表示類型爲NSNumber且爲對象類型、strong、atomic、成員變量名爲_atomicProperty。
  • 第四:在詳細描述中,屬性名稱是V開頭,後面跟着成員變量名稱。

屬性的詳細說明Property Type

###6. methodLists 對象方法列表。

/// An opaque type that represents a method in a class definition.
typedef struct objc_method *Method;

struct objc_method {
    SEL method_name;        // 方法名稱
    char *method_typesE;    // 參數和返回類型的描述字串
    IMP method_imp;         // 方法的具體的實現的指針
}

複製代碼

獲取method信息:

//方法列表
Method _Nonnull * _Nullable class_copyMethodList(Class _Nullable cls, unsigned int * _Nullable outCount) 
//方法名
SEL _Nonnull method_getName(Method _Nonnull m) 
//方法實現
IMP _Nonnull method_getImplementation(Method _Nonnull m) 
//方法參數和返回類型的信息
const char * _Nullable method_getTypeEncoding(Method _Nonnull m) 
//方法參數數量
unsigned int method_getNumberOfArguments(Method _Nonnull m)
//方法參數類型
char * _Nullable method_copyArgumentType(Method _Nonnull m, unsigned int index) 
複製代碼
  • method_getTypeEncoding獲取函數的編碼,其結果是一串值,如:v16@0:8;@16@0:8;v24@0:8@16。

這些字符串是什麼意思,經過蘋果官方文檔咱們能夠分析出:

  1. 第一個位置是返回值類型,好比第一個方法返回值是V,第二個的是@,第三個的是v
  2. 第二/三個位置是第一/二個參數,參數列表從左到右算。分別是@ :,@ :,@ :,都是對象,其實第一個和第二個參數是固定的,第一個是接收消息的對象,而第二個是方法選擇器SEL。
  3. 若是還有其它參數,依次…
  4. Type Encodings

###7. cache 用於緩存最近使用的方法。一個接收者對象接收到一個消息時,它會根據isa指針去查找可以響應這個消息的對象。在實際使用中,這個對象只有一部分方法是經常使用的,不少方法其實不多用或者根本用不上。這種狀況下,若是每次消息來時,咱們都是methodLists中遍歷一遍,性能勢必不好。這時,cache就派上用場了。在咱們每次調用過一個方法後,這個方法就會被緩存到cache列表中,下次調用的時候runtime就會優先去cache中查找,若是cache沒有,纔去methodLists中查找方法。這樣,對於那些常常用到的方法的調用,但提升了調用的效率。

參考:

  1. tech.yunyingxbs.com/article/det…
  2. developer.apple.com/library/con…
相關文章
相關標籤/搜索