Class結構總覽
Class -> objc_class -> objc_object {
// Class ISA; 8字節
Class superclass;//formerly cache pointer and vtable
cache_t cache; // class_rw_t * plus custom rr/alloc flags
class_data_bits_t bits;
class_rw_t *data() {
return bits.data();
}
void setData(class_rw_t *newData) {
bits.setData(newData);
}
... 後面就不一一列出
}
複製代碼
上圖能夠看出Class
是一個objc_object
的結構體,內部的data()
函數返回的是一個class_rw_t
的結構體:c++
"... 省略咱們暫不關心的,具體代碼請前往相關工程探索" struct class_rw_t { ... const class_ro_t *ro; method_array_t methods; property_array_t properties; protocol_array_t protocols; ... } 複製代碼
看到class_rw_t
內的結構能夠發現存儲有方法屬性等,並且有一個class_ro_t
的結構體指針。git
struct class_ro_t {
...
const char * name;
method_list_t * baseMethodList;
protocol_list_t * baseProtocols;
const ivar_list_t * ivars;
...
}
複製代碼
class_ro_t
裏邊竟然也有方法
、屬性
等參數,其實class_ro_t
就是保存類的最初始數據ro=readonly
,而class_rw_t=readwrite
,ro中的數據不容修改。github
接下來咱們經過一個項目經過objc源碼
objc源碼分析 查看 來看看究竟Class
內部結構的這些屬性中到底存了哪些內容。objective-c
TestDemo
新建一個SSObject
類繼承~NSObjectswift
#import <Foundation/Foundation.h> @interface SSObject : NSObject { NSString *_customValue; } @property (nonatomic, copy) NSString *cName; - (void)sayHello; + (void)sayHappy; @end 複製代碼
#import "SSObject.h" @implementation SSObject - (void)sayHello { } + (void)sayHappy { } @end 複製代碼
main函數以下:bash
#import "SSObject.h" #import <objc/runtime.h> int main(int argc, const char * argv[]) { @autoreleasepool { SSObject *object = [[SSObject alloc] init]; Class aClass = objc_getClass("SSObject"); NSLog(@"~~~~~~~~~~~~~~~~%p",aClass); } return 0; } 複製代碼
class_rw_t
運行項目咱們在NSLog處作斷點,經過lldb命令對內存進行打印:markdown
(lldb) x/2gx aClass // 打印aClass的內存結構 0x100001248: 0x001d800100001221 0x0000000100b00140 (lldb) po 0x100001248 // 起始地址打印的是類 SSObject (lldb) 0x100001248 + 32字節 = 0x100001268 -> class_data_bits_t 此處爲何 "+32",咱們再看objc_object的結構, objc_object { // Class ISA; 8字節 Class superclass; 8字節 cache_t cache; 16字節 class_data_bits_t bits; } 能夠看出+32的位置正好就是 class_data_bits_t 的首地址。 因爲class_data_bits_t不是常規id等類型,咱們須要強轉獲得: (lldb) p (class_data_bits_t *)0x100001268 (class_data_bits_t *) $2 = 0x0000000100001268 咱們想獲得class_rw_t的內容,即經過data()的函數 class_rw_t *data() { return bits.data(); } (lldb) p $2->data() (class_rw_t *) $3 = 0x0000000101039360 (lldb) p *$3 // 打印指針內容 (class_rw_t) $4 = { flags = 2148139008 version = 0 "ro = 0x00000001000011b0" methods = { ... } properties = { ... } protocols = { ... } firstSubclass = nil nextSiblingClass = NSDate demangledName = 0x0000000100000ef6 "SSObject" } $4 便是 "class_rw_t" 其內部的"ro" 便是"class_ro_t" 複製代碼
class_ro_t
(lldb) p $4.ro (const class_ro_t *) $16 = 0x00000001000011b0 (lldb) p *$16 (const class_ro_t) $17 = { flags = 388 instanceStart = 8 instanceSize = 24 reserved = 0 ivarLayout = 0x0000000100000eff "\x02" name = 0x0000000100000ef6 "SSObject" "baseMethodList = 0x00000001000010e8" baseProtocols = 0x0000000000000000 "ivars = 0x0000000100001150" weakIvarLayout = 0x0000000000000000 <no value available> baseProperties = 0x0000000100001198 _swiftMetadataInitializer_NEVER_USE = {} } 複製代碼
屬性
& 成員變量
咱們對class_rw_t
及class_ro_t
中的屬性和成員變量進行探索:app
(lldb) p $4.properties (property_array_t) $12 = { list_array_tt<property_t, property_list_t> = { = { list = 0x0000000100001198 arrayAndFlag = 4294971800 } } } (lldb) p $12.list "打印全部屬性列表" (property_list_t *) $13 = 0x0000000100001198 (lldb) p *$13 (property_list_t) $14 = { entsize_list_tt<property_t, property_list_t, 0> = { entsizeAndFlags = 16 count = 1 "屬性" first = (name = "cName", attributes = "T@"NSString",C,N,V_cName") } } 複製代碼
經過結果咱們能夠看到屬性cName
確實存放在class_rw_t
中,可是咱們並無看到相似成員變量的屬性,咱們接着去看上述class_ro_t
, 咱們看到相似ivars
的字段,咱們看看裏邊究竟有哪些內容。函數
(lldb) p $17.ivars (const ivar_list_t *const) $18 = 0x0000000100001150 (lldb) p *$18 (const ivar_list_t) $19 = { entsize_list_tt<ivar_t, ivar_list_t, 0> = { entsizeAndFlags = 32 "count = 2" first = { offset = 0x0000000100001218 name = 0x0000000100000f31 "_customValue" type = 0x0000000100000f6b "@"NSString"" alignment_raw = 3 size = 8 } } } 複製代碼
Note:此處咱們確實看到咱們定義在頭文件中的NSString *_customValue;
可是咱們只定義了一個成員變量,爲什麼此處count=2,打印一下便可:oop
(lldb) p $19.get(1) (ivar_t) $20 = { offset = 0x0000000100001210 name = 0x0000000100000f3e "_cName" type = 0x0000000100000f6b "@"NSString"" alignment_raw = 3 size = 8 } 複製代碼
Why: 原來是咱們屬性cName
自動生成的帶下劃線的成員變量,那麼咱們屬性和成員變量告一段落,接下來咱們看看方法。
實例方法
(lldb) p $17.baseMethodList (method_list_t *const) $21 = 0x00000001000010e8 (lldb) p *$21 (method_list_t) $22 = { entsize_list_tt<method_t, method_list_t, 3> = { entsizeAndFlags = 26 count = 4 first = { name = "sayHello" types = 0x0000000100000f50 "v16@0:8" imp = 0x0000000100000cf0 (ClassTest`-[SSObject sayHello] at SSObject.m:12) } } } 複製代碼
誠然,咱們確實看到方法列表了,可是咱們貌似只定義了- (void)sayHello;
和 + (void)sayHappy
方法,爲什麼此處count=4,打印便知:
(lldb) p $22.get(1) (method_t) $23 = { name = "cName" types = 0x0000000100000f58 "@16@0:8" imp = 0x0000000100000d10 (ClassTest`-[SSObject cName] at SSObject.h:16) } (lldb) p $22.get(2) (method_t) $24 = { name = "setCName:" types = 0x0000000100000f60 "v24@0:8@16" imp = 0x0000000100000d40 (ClassTest`-[SSObject setCName:] at SSObject.h:16) } (lldb) p $22.get(3) (method_t) $25 = { name = ".cxx_destruct" types = 0x0000000100000f50 "v16@0:8" imp = 0x0000000100000d80 (ClassTest`-[SSObject .cxx_destruct] at SSObject.m:10) } 複製代碼
原來:咱們定義屬性系統會自動幫咱們生成set
和get
方法,還有一個cxx_destruct
的c++方法是系統默認添加的,這樣加上first裏邊的sayHello
方法正好是4個,那麼問題來了,咱們的+ (void)sayHappy
的方法呢?
類方法
以前咱們瞭解過isa的相關知識,類方法保存在meta-class中,此處暫時僅以上帝視角去驗證:
(lldb) p 0x001d800100001221 & 0x00007ffffffffff8 (long) $27 = 4294971936 "元類" (lldb) x/2gx $27 0x100001220: 0x001d800100b000f1 0x0000000100b000f0 (lldb) p (class_data_bits_t *)0x100001240 (class_data_bits_t *) $28 = 0x0000000100001240 (lldb) p $28->data() (class_rw_t *) $29 = 0x00000001010647b0 (lldb) p *$29 (class_rw_t) $30 = { flags = 2685075456 version = 7 ro = 0x00000001000010a0 methods = { ... } properties = { ... } protocols = { ... } ... } (lldb) p $30.ro (const class_ro_t *) $31 = 0x00000001000010a0 (lldb) p *$31 (const class_ro_t) $32 = { flags = 389 instanceStart = 40 instanceSize = 40 reserved = 0 ivarLayout = 0x0000000000000000 <no value available> name = 0x0000000100000ef6 "SSObject" baseMethodList = 0x0000000100001080 baseProtocols = 0x0000000000000000 ivars = 0x0000000000000000 weakIvarLayout = 0x0000000000000000 <no value available> baseProperties = 0x0000000000000000 _swiftMetadataInitializer_NEVER_USE = {} } (lldb) p $32.baseMethodList (method_list_t *const) $33 = 0x0000000100001080 (lldb) p *$33 (method_list_t) $34 = { entsize_list_tt<method_t, method_list_t, 3> = { entsizeAndFlags = 26 count = 1 first = { name = "sayHappy" types = 0x0000000100000f50 "v16@0:8" imp = 0x0000000100000d00 (ClassTest`+[SSObject sayHappy] at SSObject.m:16) } } } 複製代碼
Okay如此咱們確實找到了+ (void)sayHappy
方法所在。
綜上所述,咱們分析並證明:
1.屬性會自動生成帶`_` 的成員變量。
2.若是咱們定義了屬性,系統會自動幫咱們生成set和get方法,並保存在類中。
3.靜態方法沒有保存在類中,而是元類中。
複製代碼
今天咱們就探討到這,像其中的protocol
等你們能夠繼續探索,有任何問題歡迎指正。