1. 程式人生 > >iOS執行時(runtime)探究一:重要概念

iOS執行時(runtime)探究一:重要概念

iOS執行時簡介

因為Objc是一門動態語言,所以它總是想辦法把一些決定工作從編譯連線推遲到執行時。也就是說只有編譯器是不夠的,還需要一個執行時系統 (runtime system) 來執行編譯後的程式碼。這就是 Objective-C Runtime 系統存在的意義,它是整個Objc執行框架的一塊基石。

iOS執行時的一些概念

1.類:Objective-C類是由Class型別來表示的,它實際上是一個指向objc_class結構體的指標。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;

2.例項物件:既是objc_object表示的一個類的例項的結構體,它的定義如下

struct objc_object {

    Class isa  OBJC_ISA_AVAILABILITY;

};

typedef struct objc_object *id;

3.元類(Meta Class):meta-class是一個類物件的類。它儲存著一個類的所有類方法。每個類都會有一個單獨的meta-class。

4.方法呼叫過程:首先在本類中查詢,如果沒有就向父類中查詢,然後響應該方法,最後把該方法新增到cache列表中,以後再呼叫該方法,直接從cache中取出相應的方法呼叫。

5.例項物件的isa指標指向物件所屬類,類的isa指標指向元類,元類的isa指標指向根元類(根類的元類),根元類的isa指正指向自身。類的superclass指標指向父類,根類的superclass指標指向nil,根元類的superclass指標指向根類。如下圖所示:
這裡寫圖片描述

void testClass(id self, SEL _cmd)
{
    NSObject *object = [[NSObject alloc] init];
    NSLog(@"NSObject例項:%@, 地址:%p", object, object);
    NSLog(@"NSObject類名:%@ 地址:%p,NSObject父類名:%@ 地址:%p", [object class], [object class], [object superclass], [object superclass]);

    NSLog(@"MyObject例項:%@,%p", self, self);
    NSLog(@"MyObject類名:%@ 地址:%p,MyObject父類名:%@ 地址:%p", [self class], [self class], [self superclass], [self superclass]);

    Class currentClass;
    Class superClass = [self class];
    do {
        currentClass = superClass;
        superClass = class_getSuperclass(currentClass);
        NSLog(@"當前類:%@ 地址:%p, 父類:%@ 地址:%p", currentClass, currentClass, superClass, superClass);
    } while (superClass != NULL);

    Class isaClass = [self class];
    currentClass = NULL;
    do {
        currentClass = isaClass;
        isaClass = object_getClass(currentClass);
        NSLog(@"當前類:%@ 地址:%p,isa指標指向的類:%@ 地址:%p", currentClass, currentClass, isaClass, isaClass);
    } while (!(currentClass == isaClass));
}
- (void)createClass
{
    //建立一個類,注意名稱一定不能是已經存在的類
    Class myObjectClass = objc_allocateClassPair([NSObject class], "MyObject", 0);
    //向類中新增一個方法
    class_addMethod(myObjectClass, @selector(testClass), (IMP)testClass, "v@:");
    //註冊類,註冊後方能使用該類
    objc_registerClassPair(myObjectClass);

    id myObject = [[myObjectClass alloc] init];
    [myObject performSelector:@selector(testClass)];
}

列印如下:
這裡寫圖片描述

6.super:super與self不同。self是類的一個隱藏引數,每個方法的實現的第一個引數即為self。而super並不是隱藏引數,它實際上只是一個”編譯器標示符”,它負責告訴編譯器,當呼叫viewDidLoad方法時,去呼叫父類的方法,而不是本類中的方法。而它實際上與self指向的是相同的訊息接收者。
super的定義:

struct objc_super {
     id receiver;//即訊息的實際接收者
     Class superClass;//指標當前類的父類
 };

例證:

- (void)viewDidLoad {
    [super viewDidLoad];

    NSLog(@"self:%@ super:%@", self, super.class);
}

列印如下:
這裡寫圖片描述

7.SEL:SEL又叫選擇器,是表示一個方法的selector的指標,本質上,SEL只是一個指向方法的指標(準確的說,只是一個根據方法名hash化了的KEY值,能唯一代表一個方法),它的存在只是為了加快方法的查詢速度。其定義如下:

typedef struct objc_selector *SEL;

8.IMP:IMP實際上是一個函式指標,指向方法實現的首地址。SEL就是為了查詢方法的最終實現IMP的。由於每個方法對應唯一的SEL,因此我們可以通過SEL方便快速準確地獲得它所對應的IMP,查詢過程將在下面討論。取得IMP後,我們就獲得了執行這個方法程式碼的入口點,此時,我們就可以像呼叫普通的C語言函式一樣來使用這個函式指標了。其定義如下:

id (*IMP)(id, SEL, ...)

9.Method:既是objc_method表示的一個結構體,該結構體中包含一個SEL和IMP,實際上相當於在SEL和IMP之間作了一個對映。有了SEL,我們便可以找到對應的IMP,從而呼叫方法的實現程式碼。Method用於表示類定義中的方法,則定義如下:

typedef struct objc_method *Method;



struct objc_method {

    SEL method_name                 OBJC2_UNAVAILABLE;  // 方法名

    char *method_types                  OBJC2_UNAVAILABLE;

    IMP method_imp                      OBJC2_UNAVAILABLE;  // 方法實現

}