健壯的實例變量 (Non Fragile ivars)和脆弱的實例變量(Fragile ivars)

一、Non Fragile ivars

objc_class 結構體bash

struct objc_class {
    Class isa  OBJC_ISA_AVAILABILITY;
 
#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;

複製代碼

objc_ivar結構體app

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;

複製代碼

對象地址 + ivar偏移字節
在編譯類時,編譯器生成了一個 ivar 佈局,顯示了在類中從哪能夠訪問ivars 對 ivar 的訪問就能夠經過 對象地址 + ivar偏移字節的方法

父類增長實例變量

蘋果更新了NSObject類,發佈新版本的系統,當增長了父類的ivar,這個時候佈局就出錯了,就不得不從新編譯子類來恢復兼容性。 (那若是是在線上運行的app,升級系統後就沒辦法運行了)函數

兼容父類的變化

使用 Non Fragile ivars時,Runtime會進行檢測來調整類中新增的 ivar 的偏移量。 這樣就能夠經過對象地址 + 基類大小 + ivar偏移字節 的方法來計算出ivar相應的地址,並訪問到相應的ivar。(即便升級iOS系統,以前的app也能正常運行)佈局

二、Non Fragile ivars的意義

Objective-C的庫今後具備了**「二進制兼容性」**。 好比在項目裏用了第三方提供的靜態庫SDK,包含一些.h和一個.a文件。當iOS SDK的版本從11升到了12,都不須要更新這個SDK。雖然iOS SDK版本升級時,蘋果在等基類中加入了更多的成員變量,可是之前發佈的靜態庫SDK不須要從新編譯還能正常使用。spa

iOS從一開始就是用的modern runtime。 之前的Mac開發者每次MacOS發佈新版本,都要從新編譯本身的程序,跟着發佈新版本。3d

三、引伸-爲何OC類不能動態添加成員變量

既然容許用Category給類增長方法和屬性,那爲何不容許增長成員變量? 在Objective-C提供的runtime函數中,確實有一個class_addIvar()函數用於給類添加成員變量,可是文檔中特別說明:指針

This function may only be called after objc_allocateClassPair and before objc_registerClassPair. Adding an instance variable to an existing class is not supported.code

這個函數只能在「構建一個類的過程當中」調用。 一旦完成類定義,就不能再添加成員變量了。 通過編譯的類在程序啓動後就被runtime加載,沒有機會調用addIvar。 程序在運行時動態構建的類須要在調用objc_registerClassPair以後才能夠被使用,一樣沒有機會再添加成員變量。cdn

爲基類動態增長成員變量會致使全部已建立出的子類實例都沒法使用。對象

那爲何runtime容許動態添加方法和屬性,而不會引起問題呢?

由於方法和屬性並不「屬於」類實例,而成員變量「屬於」類實例。咱們所說的「類實例」概念,指的是一塊內存區域,包含了isa指針和全部的成員變量。 因此假如容許動態修改類成員變量佈局,已經建立出的類實例就不符合類定義了,變成了無效對象。 但方法定義是在objc_class中管理的,無論如何增刪類方法,都不影響類實例的內存佈局,已經建立出的類實例仍然可正常使用。

相關文章
相關標籤/搜索