iOS runtime探究(一): 從runtime開始理解面向對象的類到面向過程的結構體

你要知道的runtime都在這里

轉載請注明出處 http://www.lxweimin.com/p/17e158a666b1

本文主要講解runtime相關知識,從原理到實踐,由于包含內容過多分為以下五篇文章詳細講解,可自行選擇需要了解的方向:

本文是系列文章的第一篇文章從runtime開始: 理解面向對象的類到面向過程的結構體,主要從runtime出發講解面向對象的類是如何轉變為面向過程的結構體,來探究OC對類的處理本質。

什么是runtime

runtime就是運行時,在實際開發中使用runtime的場景并不多,但是了解runtime有助于我們更好的理解OC的原理,從而提高開發水平。
runtime很強大,是OC最重要的一部分也是OC最大的特色,可以不夸張的說runtime成就了OC,盡管runtime是OC的一個模塊而已。
我們都知道高級編程語言想要成為可執行文件需要先編譯為匯編語言再匯編為機器語言,機器語言也是計算機能夠識別的唯一語言,但是OC并不能直接編譯為匯編語言,而是要先轉寫為純C語言再進行編譯和匯編的操作,從OC到C語言的過渡就是由runtime來實現的。然而我們使用OC進行面向對象開發,而C語言更多的是面向過程開發,這就需要將面向對象的類轉變為面向過程的結構體,本文正是通過runtime源碼分析來講解runtime是如何將面向對象的類轉變為面向過程的結構體。

深入代碼理解instance、class object、metaclass

面向對象編程中,最重要的概念就是類,下面我們就從代碼入手,看看OC是如何實現類的。

前文一直在說runtime將面向對象的類轉變為面向過程的結構體,那這個結構體到底是什么樣子的?打開#import<objc/objc.h>文件,可以發現以下幾行代碼

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

/// Represents an instance of a class.
struct objc_object {
    Class isa  OBJC_ISA_AVAILABILITY;
};

/// A pointer to an instance of a class.
typedef struct objc_object *id;

通過注釋和代碼不難發現,我們創建的一個對象或實例其實就是一個struct objc_object結構體,而我們常用的id也就是這個結構體的指針。有如下代碼:

//以下兩種寫法都成立
id str = [[NSString alloc] init];
NSString *str = [[NSString alloc] init];

通過上述代碼可以看出,我們創建的NSString類的實例str其實就是一個struct objc_object結構體指針,所以不管是Foundation框架中的類或是自定義的類,我們創建的類的實例最終獲取的都是一個結構體指針,這個結構體只有一個成員變量就是Class類型的isa指針,Class是結構體指針,指向struct objc_class,那這個結構體又是什么呢?這里先透露一句話str is a NSString,再加上Class這個指針的名字,我們不難猜測,Class就是代表NSString這個類。
接下來會詳細講解這個結構體,現在再看另一個例子,有時我們也會通過下述方法來創建一個實例:

NSString *str = [[NSString alloc] initWithString: @"Hello World"];
Class c = [str class];
NSString *str2 = [[c alloc] initWithString: @"Hello World"];

可能你已經發現了,通過實例對象調用的class方法,我們能夠獲取到一個Class類型的變量,我們可以通過這個Class來創建相應的實例對象。
實際上,OC中的類也是一個對象,稱為類對象,上述方法中通過[str class]方法獲取到的就是NSString類類對象,接著我們就可以通過這個類對象來創建實例對象,那這個類對象又是什么東西呢?打開#import<objc/runtime.h>文件,我們可以找到結構體struct objc_class的定義,該結構體定義如下:

文件objc/runtime.h中有如下定義:
struct objc_class {
    Class isa  OBJC_ISA_AVAILABILITY;

    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                     
}
/* Use `Class` instead of `struct objc_class *` */

文件objc/objc.h文件中有如下定義
/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;

/// Represents an instance of a class.
struct objc_object {
    Class isa  OBJC_ISA_AVAILABILITY;
};

/// A pointer to an instance of a class.
typedef struct objc_object *id;

struct objc_class結構體定義了很多變量,通過命名不難發現,結構體里保存了指向父類的指針、類的名字、版本、實例大小、實例變量列表、方法列表、緩存、遵守的協議列表等,一個類包含的信息也不就正是這些嗎?沒錯,類對象就是一個結構體struct objc_class,這個結構體存放的數據稱為元數據(metadata),該結構體的第一個成員變量也是isa指針,這就說明了Class本身其實也是一個對象,因此我們稱之為類對象類對象在編譯期產生用于創建實例對象,是單例。

類對象中的元數據存儲的都是如何創建一個實例的相關信息,那么類對象類方法應該從哪里創建呢?就是從isa指針指向的結構體創建,類對象isa指針指向的我們稱之為元類(metaclass)元類中保存了創建類對象以及類方法所需的所有信息,因此整個結構應該如下圖所示:

實例對象、類對象與元類簡圖

通過上圖我們可以清晰的看出來一個實例對象也就是struct objc_object結構體它的isa指針指向類對象類對象isa指針指向了元類,super_class指針指向了父類的類對象,而元類super_class指針指向了父類的元類,那元類isa指針又指向了什么?為了更清晰的表達直接使用一個大神畫的圖。

實例對象、類對象與元類的自閉環

通過上圖我們可以看出整個體系構成了一個自閉環,如果是從NSObject中繼承而來的上圖中的Root class就是NSObject。至此,整個實例類對象元類的概念也就講清了,接下來我們在代碼中看看這些概念該怎么應用。

@interface Person : NSObject

@property (nonatomic, copy) NSString* name;
@property (nonatomic, assign) NSUInteger age;

@end

@implementation Person

@synthesize name = _name;
@synthesize age = _age;

@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *p = [[Person alloc] init];
        Class c1 = [p class];
        Class c2 = [Person class];
        //輸出 1
        NSLog(@"%d", c1 == c2);
    }
    return 0;
}

c1是通過一個實例對象獲取的Class,實例對象可以獲取到其類對象,類名作為消息的接受者時代表的是類對象,因此類對象獲取Class得到的是其本身,同時也印證了類對象是一個單例的想法。
那么如果我們想獲取isa指針的指向對象呢?

介紹兩個函數

OBJC_EXPORT BOOL class_isMetaClass(Class cls) 
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
    
OBJC_EXPORT Class object_getClass(id obj) 
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);

class_isMetaClass用于判斷Class對象是否為元類object_getClass用于獲取對象的isa指針指向的對象。

再看如下代碼:

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *p = [[Person alloc] init];
        //輸出1
        NSLog(@"%d", [p class] == object_getClass(p));
        //輸出0
        NSLog(@"%d", class_isMetaClass(object_getClass(p)));
        //輸出1
        NSLog(@"%d", class_isMetaClass(object_getClass([Person class])));
        //輸出0
        NSLog(@"%d", object_getClass(p) == object_getClass([Person class]));
    }
    return 0;
}

通過代碼可以看出,一個實例對象通過class方法獲取的Class就是它的isa指針指向的類對象,而類對象不是元類類對象isa指針指向的對象是元類

總結

通過上文的代碼分析,我們已經了解了OC中的類和實例是如何映射到C語言結構體的,實例對象是一個結構體,這個結構體只有一個成員變量,指向構造它的那個類對象,這個類對象中存儲了一切實例對象需要的信息包括實例變量、實例方法等,而類對象是通過元類創建的,元類中保存了類變量和類方法,這樣就完美解釋了整個類和實例是如何映射到結構體的。

下一步

了解類到結構體映射只是揭開runtime神秘面紗的第一步,下一篇博客將會介紹OC的消息傳遞機制以及runtime對OC消息傳遞所做的具體操作,感興趣的讀者可以繼續學習下一篇文章從runtime開始: 深入理解OC消息轉發機制

備注

由于作者水平有限,難免出現紕漏,如有問題還請不吝賜教。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,786評論 6 534
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,656評論 3 419
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,697評論 0 379
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,098評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,855評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,254評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,322評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,473評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,014評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,833評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,016評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,568評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,273評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,680評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,946評論 1 288
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,730評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,006評論 2 374

推薦閱讀更多精彩內容