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 佈局,顯示了在類中從哪能夠訪問ivars 對 ivar 的訪問就能夠經過
對象地址 + ivar偏移字節
的方法
蘋果更新了NSObject類,發佈新版本的系統,當增長了父類的ivar,這個時候佈局就出錯了,就不得不從新編譯子類來恢復兼容性。 (那若是是在線上運行的app,升級系統後就沒辦法運行了)函數
使用 Non Fragile ivars
時,Runtime會進行檢測來調整類中新增的 ivar 的偏移量。 這樣就能夠經過對象地址 + 基類大小 + ivar偏移字節
的方法來計算出ivar相應的地址,並訪問到相應的ivar。(即便升級iOS系統,以前的app也能正常運行)佈局
Objective-C的庫今後具備了**「二進制兼容性」**。 好比在項目裏用了第三方提供的靜態庫SDK,包含一些.h和一個.a文件。當iOS SDK的版本從11升到了12,都不須要更新這個SDK。雖然iOS SDK版本升級時,蘋果在等基類中加入了更多的成員變量,可是之前發佈的靜態庫SDK不須要從新編譯還能正常使用。spa
iOS從一開始就是用的modern runtime。 之前的Mac開發者每次MacOS發佈新版本,都要從新編譯本身的程序,跟着發佈新版本。3d
既然容許用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中管理的,無論如何增刪類方法,都不影響類實例的內存佈局,已經建立出的類實例仍然可正常使用。