轉:Object-Runtime的基礎資料型別 (Elementary Data Type)

來源:互聯網
上載者:User

標籤:blog   http   使用   strong   檔案   資料   

Class

Objective-C是支援反射的,先來瞭解一下其如何表達一個類。在Objective-C的Runtime中有個類型是Class(只在Runtime環境中使用),用來表示Objective-C中的類,其定義為:

typedef struct objc_class *Class;

可以看出,其實Class類型是一個指標,指向struct  objc_class,而struct  objc_class才是儲存真正資料的地方,再看struct  objc_class的聲明(from http://www.opensource.apple.com/source/objc4/objc4-493.9/runtime/runtime.h):

struct objc_class {    Class isa;#if !__OBJC2__    Class super_class                                        OBJC2_UNAVAILABLE;    const char *name                                         OBJC2_UNAVAILABLE;    long version                                             OBJC2_UNAVAILABLE;    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;

其中包含了方法列表、父類等資訊,詳細的可以稍後再看。

 

Method

是Runtime內部定義的方法,用來代表一個方法,其聲明如下:

typedef struct objc_method *Method;

而struct  objc_method的聲明如下:

struct objc_method {    SEL method_name                                          OBJC2_UNAVAILABLE;    char *method_types                                       OBJC2_UNAVAILABLE;    IMP method_imp                                           OBJC2_UNAVAILABLE;}

SEL和IMP代表什麼需要看下面的內容。如果你已經瞭解了SEL和IMP的含義,可以看看下面這段:根據Class和Method的定義來理解Objective C中的訊息機制:

先看看objc_class中method list的在runtime(http://opensource.apple.com/source/objc4/objc4-437/runtime/objc-runtime-new.h)裡的定義:

typedef struct method_list_t {    uint32_t entsize_NEVER_USE;  // low 2 bits used for fixup markers    uint32_t count;    struct method_t first;} method_list_t;
typedef struct method_t { SEL name; const char *types; IMP imp;} method_t;

SEL相當於char*,可以認為objc_class中method list儲存了一個SEL<->IMP的映射,看下面的代碼:

Bird * aBird = [[Bird alloc] init];[aBird fly];

其中對fly的調用,其實是由編譯器插入了一些代碼,根據SEL([aBird fly] 中的fly就是SEL)找到了IMP,從而進行調用的。下面看編譯器插入了什麼樣的代碼。我們來看Objective C runtime中跟msg相關的函數:

id objc_msgSend(id theReceiver, SEL theSelector, ...)

這個函數發送訊息給theReceiver,並將傳回值返回。編譯器其實就是將[aBird fly]轉化成了對objc_msgSend的調用,從而實現訊息機制的。objec_msgSend()函數將會使用theReceiver的isa指標來找到theReceiver的類空間結構並在類空間結構中尋找theSelector所對應的方法。如果沒有找到,那麼將使用指向父類的指標找到父類空間結構進行theSelector的尋找。如果仍然沒有找到,就繼續往父類的父類一直找,直到找到為止。如果找不到怎麼辦呢?關於訊息機制,有一篇引用文章,介紹的更加詳細,這裡就不贅述。

 

Ivar

Runtime中用來表示instance variable(執行個體變數,跟某個對象關聯,不能被靜態方法使用,與之想對應的是class variable),其聲明如下:

typedef struct objc_ivar *Ivar;

而struct objc_ivar的聲明如下:

struct objc_ivar {    char *ivar_name                                          OBJC2_UNAVAILABLE;    char *ivar_type                                          OBJC2_UNAVAILABLE;    int ivar_offset                                          OBJC2_UNAVAILABLE;#ifdef __LP64__    int space                                                OBJC2_UNAVAILABLE;#endif}                                                            OBJC2_UNAVAILABLE;

Category

Runtime中用來表示Category( link ?),其聲明為:

typedef struct objc_category *Category;

struct objc_category 的定義也在runtime.h檔案中。

[]Catagory可以動態地為已經存在的類添加新的行為。這樣可以保證類的原始設計規模較小,功能增加時再逐步擴充。使用Category對類進行擴充時,不需要訪問其原始碼,也不需要建立子類。Category使用簡單的方式,實現了類的相關方法的模組化,把不同的類方法分配到不同的分類檔案中。下面看一個例子:

SomeClass.h@interface SomeClass : NSObject{}-(void) print;@end 

這是類SomeClass的聲明檔案,其中包含一個執行個體方法print。如果我們想在不修改原始類、不增加子類的情況下,為該類增加一個hello的方法,只需要簡單的定義兩個檔案SomeClass+Hello.h和SomeClass+Hello.m,在聲明檔案和實現檔案中用“()”把Category的名稱括起來即可。聲明檔案代碼如下:

#import "SomeClass.h" @interface SomeClass (Hello)-(void)hello;@end

實現檔案代碼如下:

#import "SomeClass+Hello.h"@implementationSomeClass (Hello)-(void)hello{    NSLog (@"name:%@ ", @"Jacky");}@end

其中Hello是Category的名稱,如果你用XCode建立Category,那麼需要填寫的內容包括名稱和要擴充的類的名稱。這裡還有一個約定成俗的習慣,將聲明檔案和實現檔案名稱統一採用“原類名+Category”的方式命名。
調用也非常簡單,毫無壓力,首先引入Category的聲明檔案,然後正常調用即可:

#import "SomeClass+Hello.h" SomeClass * sc =[[SomeClass alloc] init];[sc hello] 

執行結果是:
name:Jacky 

Category的使用情境:
1、當你在定義類的時候,在某些情況下(例如需求變更),你可能想要為其中的某個或幾個類中添加方法。
2、一個類中包含了許多不同的方法需要實現,而這些方法需要不同團隊的成員實現
3、當你在使用基礎類庫中的類時,你可能希望這些類實現一些你需要的方法。

遇到以上這些需求,Category可以協助你解決問題。當然,使用Category也有些問題需要注意,
1、Category可以訪問原始類的執行個體變數,但不能添加變數,如果想添加變數,可以考慮通過繼承建立子類。
2、Category可以重載原始類的方法,但不推薦這麼做,這麼做的後果是你再也不能訪問原來的方法。如果確實要重載,正確的選擇是建立子類。
3、和普通介面有所區別的是,在分類的實現檔案中可以不必實現所有聲明的方法,只要你不去調用它。

用好Category可以充分利用Objective-C的動態特性,編寫出靈活簡潔的代碼。

 

SEL

Runtime中用來表示一個method selector,其聲明為:

typedef struct objc_selector     *SEL;

沒有找到struct objc_selector的定義,有人說是編譯器定義的,GCC 和MacOSX的實現方式還不一樣,不想花時間找GCC的代碼,而且也沒那麼重要,所以就先姑且相信這個說法吧。

IMP

IMP是一個函數指標,指向方法的實現,其定義為:

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

其所指向的方法,返回一個id(Cocoa 對象),需要傳入的第一個參數是self(指向某個對象,或者一個類),第二個參數是方法的SEL。

objc_property_t

objc_method_list

objc_cache

objc_protocol_list

id

在 Objective-C中id類型的對象可以轉換為任何一種對象,有點類似與void *指標類型的作用。下面簡要介紹一下id類型。

id標誌符:通用物件類型。id類型是一個獨特的資料類型,可以轉換為任何資料類型,即id類型的變數可以存放任何資料類型的對象。id在objc.h中的定義為:

typedef struct objc_object {    Class isa;} *id;

從上面的介紹,我們已經知道Class是struct  objc_class的指標別名,所以id可以指向一個第一個元素是Class的struct;那麼它為什麼可以指向NSObject對象呢?下面看NSObject的定義:

@interface NSObject <NSObject> {    Class    isa;}

可以看出NSObject的第一個對象是Class類型的isa。因為第一個元素相同,也就意味著可以互相cast而不損失資訊,下面是用C語言來示範的其實現原理:

#include <stdio.h>#include <stdlib.h>struct objc_class{    int count;    char * name;};typedef struct objc_class * Class;typedef struct objc_obj0{    Class isa;}*id;typedef struct objc_obj1{    Class isa;    int a;}*id1;typedef struct objc_obj2{    Class isa;    char *b;}*id2;int main(int argc, char **argv){    // id 的第一個元素與id1是一樣的,所以可以用id指向id1的元素,而不損失任何資訊,不過後續使用的時候應該使用其實際類型    id a = (struct objc_obj1 *)malloc(sizeof(struct objc_obj1));     id b = (struct objc_obj1 *)malloc(sizeof(struct objc_obj1)); }

實施上,通常而言,這樣使用時編譯器是要report warning的,我們可以在.m檔案中加入下面的代碼來驗證:

typedef struct objc_object *id2;id2 = [[NSNumber alloc] initWithInt:(i*3)];

這時是會報incompatible pointer types initializing ‘id2‘ (aka ‘struct objc_object *‘) with an expression of type ‘NSNumber *‘ 的,但id2和id的定義相同,為什麼使用id時不會有這個warning呢?因為編譯器對id做了特殊處理,不報warning。這下對id有了更多瞭解了吧。後續而來的問題就是,為什麼可以在id類型上調用一些NSNumber上才有的方法呢?這一部分留到Dynamic Typing and Dynamic binding時再說吧。 

 

http://unixjunkie.blogspot.com/2008/03/id-vs-nsobject-vs-id.html

http://www.cppblog.com/kesalin/archive/2011/08/15/objc_message.html

http://www.cnblogs.com/chijianqiang/archive/2012/06/22/objc-category-protocol.html

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.