iOS RunTime 總結

LuckyRoc發表於2019-01-10

RunTime

RunTime簡稱執行時。就是系統在執行的時候的一些機制,其中最主要的是訊息機制

RunTime的2個重要特種

  • C++等語言,在編譯時就已經確定了,執行時就是找到記憶體的位置,然後執行程式碼;而在Objective-C中,方法的呼叫實際上是以一種叫“訊息轉發”的方式進行的,也就是告訴class/object,我要呼叫某個object/class的某個方法;但是!具體是否呼叫某個方法,如何呼叫,可以在執行時決定和修改。
  • class/object/method…的本質都是struct,因此在執行時也可以進行修改。

Class

先來看下 Class 的定義:

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

實際上Class就是 objc_class

objc_class

在<objc/runtime.h>中,可以找到一個叫objc_class的struct。我們看一下這個struct的定義

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;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    //ivar列表,是一個objc_ivar_list型別的指標 代表例項變數
    struct objc_ivar_list * _Nullable ivars                  OBJC2_UNAVAILABLE;
    // method列表,是一個objc_method_list二級指標
    struct objc_method_list * _Nullable * _Nullable methodLists                 OBJC2_UNAVAILABLE;
    //儲存常用method,加快訊息轉發速度
    struct objc_cache * _Nonnull cache                       OBJC2_UNAVAILABLE;
    //儲存實現的protocols
    struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;
#endif

} OBJC2_UNAVAILABLE;
複製程式碼

平常我們定義了一個class之後,實際上它會被編譯成一個這樣的struct;我們定義的變數、方法等,都儲存在struct中。

objc_object

/// Represents an instance of a class.
struct objc_object {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
};
複製程式碼

當我們建立了一個類的例項後,其實生成了一個這樣的struct。它裡面的的Class isa,可以在訊息轉發時尋找到對應的class

objc_method_list

看一下 objc_class 中的 objc_method_list 定義

struct objc_method_list {
    int method_count;
    /* variable length structure */
    struct objc_method method_list[1];
}
複製程式碼

objc_method_list 裡面儲存了一個 objc_method

objc_method

struct objc_method {
    SEL method_name;
    char *method_types;
    IMP method_imp;
} 
複製程式碼

這個objc_method就是我們熟悉的method。可以看到method主要由方法名、function指標組成,將SELIMP做了一個對映

SEL

typedef struct objc_selector *SEL;
複製程式碼

objc_selector就是一個C字串。因此,SEL其實就是一個字串,可以理解成是對方法名的hash化

IMP

typedef void (*IMP)(void /* id, SEL, ... */ ); 
複製程式碼

IMP是方法的具體實現

當我們呼叫某個方法時,其實是向這個object傳送一個“訊息”,包含了方法名和引數。通過isa 找到objc_class, 然後在objc_class中遍歷尋找對應的objc_method,找到之後再呼叫IMP。 也就是說,方法名SEl和真正的實現程式碼IMP之間,是由objc_method來繫結的,這樣就實現了相對的動態化。

訊息傳遞具體流程

iOS RunTime 總結

  • 將方法呼叫轉化成objc_msgSend等方法,向object傳送訊息;
  • 通過objc_objectisa找到其所在class;
  • 檢測這個SEL是不是要忽略的;比如ARC中會忽略retain等操作;
  • 檢測target是否是nil;如果是的話,就忽略掉;
  • 從cache中找SEL;一旦找到,就執行SEL對應的IMP
  • 如果沒有,再到methodLists裡找;
  • 如果還沒有,再到superclass裡找,一直找到NSObject
  • 如果還沒有,進入動態方法解析。

相關文章