閱讀904 返回首頁    go 技術社區[雲棲]


iOS開發中的對象係統基礎

[0] Outline

  -- [1] id和Class

  -- [2] 動態地操作類

  -- [3] 實例化


[1] id和Class

在Objective-C中有一個特別的數據類型作為對象標識符:id,可以指向任何類型的對象。

通過 “可以指向任何類型的對象” 這一描述,猜想id實際上是指向Objective-C對象係統中的基類(繼承體係中的祖先結構)的指針,在運行時是指向對象內存布局的基類部分。


第一眼看到id數據類型,我聯想到了Python中的PyObject結構:

typedef struct _object {
    int ob_refcnt;
    struct _typeobject *ob_type;
} PyObject;

該數據類型也是Python對象係統中的祖先類型,不過與id相對應的應該是PyObject *類型。


id數據類型是一個指向struct objc_object結構的指針:

typedef struct objc_class *Class;
typedef struct objc_object {
    Class isa;
} *id;

更確切地說,id是指向Class類型的指針,而Class又是指向struct objc_class結構的指針:

struct objc_class {
     struct objc_class *isa;

     struct objc_class *super_class; 
     const char *name;  
     long version;
     long info;
     long instance_size;
     struct objc_ivar_list *ivars;
     struct objc_method_list **methodLists;
     struct objc_cache *cache;
     struct objc_protocol_list *protocols;
};

至此,可以看到Objective-C對象係統的基石:struct objc_class結構:

     isa指針:指向該對象所屬類型的類型對象(Class Object)。在Objective-C中,類也是用對象來表示的,而類的isa指針指向它的metaclass(存儲靜態成員變量和類方法)。
     super_class指針:指向父類。
     name:類名稱。
     version:類的版本信息。
     info:運行期使用的標誌位,比如0x1(CLS_CLASS)表示該類為普通class,0x2(CLS_META)表示該類為     metaclass。
     instance_size:實例大小,即內存所占空間。
     ivars:指向成員變量列表的指針。
     methodLists:根據標誌位的不同可能指向不同,比如可能指向實例方法列表,或者指向類方法列表。
     cache:因為Objective-C的消息轉發需要查找dispatch table甚至可能需要遍曆繼承體係,所以緩存最近使用的方法。
     protocols:類需要遵守的協議。

[2] 動態地操作類

由上知道了類也是一種對象,那麼類對象也有一種類型,這種類型就是類的metaclass,因此類方法其實就是metaclass的成員方法,類和metaclass是配套出現的。

那麼metaclass的isa指針和super_class指針怎麼指向的呢?

如果metaclass對應基類,那麼它的isa指向自身、super_class指針指向對應的類(基類);如果不是則isa指針指向基類的metaclass、super_class指針指向父類的metaclass。

基類的isa指針為nil。


這不禁讓我又想起了Python中類似的設計思想,比如整型數字2的類型是PyIntObject,而PyIntObject類的類型是PyTypeObject,PyTypeObject的類型是PyTypeObject。最終止於此。

同樣地,Python中也有metaclass的存在。


知道了類的表示結構,我們可以動態地對類進行操作,加深理解。

//
//  main.m
//  HelloOC
//
//  Created by Jason Lee on 12-2-17.
//  Copyright (c) 2012年 XXX. All rights reserved.
//

#import <Foundation/Foundation.h>

#import <objc/objc.h>
#import <objc/runtime.h>

void selfIntro(idself, SEL_cmd);

int main (int argc, constchar * argv[])
{
    
    @autoreleasepool {
        //Create class & metaclass as a pair
        Class demoClass = objc_allocateClassPair([NSObjectclass], "NSDemo", 0);
        
        BOOL isOk = NO;
        
        //"v@:" indicates the function type : v - void, @ - object, : - SEL
        //向methodLists指向添加數據
        isOk = class_addMethod(demoClass, @selector(intro), (IMP)&selfIntro, "v@:");
        isOk == YES ? nil : NSLog(@"failed on class_addMethod\n");
        
        //向ivars指向添加數據
        isOk = class_addIvar(demoClass,"myVar", sizeof(id), log2(sizeof(id)), "@"); 
        isOk == YES ? nil : NSLog(@"failed on class_addIvar\n");
        
        objc_registerClassPair(demoClass);
        
        id demo = class_createInstance(demoClass, 0);
        
        if ([demo respondsToSelector:@selector(intro)]) {
            [demo performSelector:@selector(intro)];
        }
        
        object_setInstanceVariable(demo, "myVar", nil);
        void *outValue = (void *)0x1;
        object_getInstanceVariable(demo, "myVar", &outValue);
        if (nil == outValue) {
            NSLog(@"Hello,nil\n");  //printed
        }
        
        object_dispose(demo);
    }
    
    return0;
}

void selfIntro(idself, SEL_cmd) {
    NSLog(@"selfIntro\n");
    
    Class isa = self->isa;  //At first, isa is class NSDemo
    while (1) {
        if (isa == isa->isa) {  //Finally, NSObject's metaclass points to itself
            NSLog(@"%p, %@", isa, objc_getMetaClass(class_getName([isa class])));
            break;
        }
        
        NSLog(@"%p, %@", isa, objc_getMetaClass(class_getName([isa class])));
        isa = isa->isa; //Then, isa is assigned to NSDemo's metaclass
    }
}


[3] 實例化

要實例化出一個對象,需要根據類的定義來進行。類的定義包括類名稱、數據和操作數據的方法。

編譯過程,類的信息會被記錄下來,供runtime system使用,同時編譯器會為每個類創建唯一的一個對象來表示它:class object。如果從功能上說,class object也是factory object,它能夠執行類方法,負責創建實例。


從這個角度來看,我在思考class object是否就是metaclass,但是不能確認。

Apple官方文檔TOCPL中Class Objects一章有這麼一句,“a class object keeps the prototype of a class instance”,但metaclass並不能作為實例原型。

於是我認為class object是運行時class和metaclass結合起來的受限表現,能夠訪問編譯器捕捉下來的類信息,沒有成員變量,不能調用成員方法,但是可以執行類方法。

從源碼層次來看,class object是由類名來表示,比如下述代碼中:

int version = [NSString version];

NSString代表著class object。


首先,class object會被runtime system發送initialize消息進行初始化,讓其做好運行時的準備,比如初始化靜態變量。

之後,可以調用class object的方法(類方法)alloc來為新的實例對象分配內存空間,將其所有變量初始化為0,isa指針指向所屬類。

最後再調用init函數進行必要的初始化。


寫到這裏的時候,突然要變更辦公位置,思路被打斷了,就先寫到這裏。

最後,留一個在SO上麵看到的問題,我也疑惑,隻能有幾分猜測:https://stackoverflow.com/questions/8847146/whats-is-methodlists-attribute-of-the-structure-objc-class-for

[這篇文章是我對SO上的問題的解答:https://blog.csdn.net/jasonblog/article/details/7303618]


[Last Updated] 2012-03-17


Jason Lee @ 杭州

博客:https://blog.csdn.net/jasonblog

微博:https://weibo.com/jasonmblog

GitHub: https://github.com/siqin

最後更新:2017-04-02 22:16:24

  上一篇:go ITEXT實例學習與研究(二) 之 創建一個細長的淺黃色背景的頁麵以及縱向頁麵與橫向頁麵之間的切換
  下一篇:go 《android2.2自帶通訊錄》之聯係人按拚音排序以及按漢字首字母或全拚搜索