一、概述 如上圖: 1.記憶體創建一個instance實例對象(Person *per),同時會創建一個與之對應的類對象(Class perClass)和元類對象(Class perMeta); 註:實例對象通過calloc可創建多個,但類對象和元類對象在記憶體中只有一份,只創建一次; 2.對象的本質, ...
一、概述
如上圖:
1.記憶體創建一個instance實例對象(Person *per),同時會創建一個與之對應的類對象(Class perClass)和元類對象(Class perMeta);
註:實例對象通過calloc可創建多個,但類對象和元類對象在記憶體中只有一份,只創建一次;
2.對象的本質,其實是C語言的結構體struct,各個對象的記憶體結構為:
per:isa指針+僅存儲Person類成員變數的值;
Person:isa指針+superclass指針+存儲成員變數的類型、名稱,協議,對象方法等;
perMeta:isa指針+superclass指針+僅存儲類方法;
3.isa指向:
per:指向類對象Person;
Person:指向元類對象perMeta;
perMeta:指向基類(Root,如:NSObject)的元類對象meta(基類的元類對象的isa指向該元類對象自己);
4.superclass指向:
Person:指向父類>>基類的類對象指向nil;
perMeta:指向父類>>基類的元類對象指向該基類的類對象;
二、代碼分析
1)通過實例對象per獲取類對象
- (void)viewDidLoad {
[super viewDidLoad];
self.per1 = [[Person alloc] init];
self.per2 = [[Person alloc] init];
//類對象
Class perClass1 = [self.per1 class];
Class perGetClass2 = object_getClass(self.per1);
Class person = [Person class];
//打斷點
NSLog(@"---");
}
進入lldb模式:
//所有的對象(包括類對象和實例對象)所屬類的名字均為“Person”
//查看類對象自身地址和self.per成員變數isa的地址值
//p/x:以十六進位輸出
如上圖說明三個問題:
第一,每個實例對象開闢單獨的記憶體;
第二,同一種類對象僅在記憶體中開闢一次;
第三,此處class方法和object_getClass無任何區別;
2)通過類對象獲取元類對象
增加代碼:
//元類對象 Class perMeta1 = [perGetClass2 class]; Class perMeta2 = object_getClass(perGetClass2);
lldb模式:
報錯:引用的成員變數isa不是結構體或共同體的成員;
原因:結構體中的isa變數沒有暴露出來,從而無法引用;
解決:自定義相同結構體,並將對象強制轉換
//添加代碼
struct lyb_objc_class { Class _Nonnull isa; }; struct lyb_objc_class *perGetClass3 = (__bridge struct lyb_objc_class *)object_getClass(self.per1);
//lldb模式:
說明:
1.perGetClass2和perGetClass3指的是同一個類對象;
2.perMeta1的地址跟perGetClass2和perGetClass3的地址是相同的,說明此時class並沒有返回元類對象,依然是類對象;
3.perMeta2的地址和perGetClass3->isa指向的地址相同,說明object_getClass返回的是元類對象;
4.元類對象的類名稱和類對象的一樣,依然是Person;
3)通過元類對象獲取基類的元類對象
//添加代碼
struct lyb_objc_class *perMeta3 = (__bridge struct lyb_objc_class *)object_getClass(perGetClass2);
//還是perMeta2
Class rootMeta1 = [perMeta2 class];
//基類(NSObject)的元類對象
Class rootMeta2 = object_getClass(perMeta2);
//lldb模式:
說明:
1.class返回的依然是元類對象自身,object_getClass返回的是基類的元類對象;
2.基類的元類對象的類名跟類對象的一樣,為NSObject;
三、結論
當消息對象為實例對象instance時,class與object_getClass返回的對象地址一樣;當消息對象為類對象,或元類對象時,class返回的消息對象本身,而object_getClass返回的是下一個對象;
原因:因為class返回的是self,而object_getClass返回的是isa指向的對象;
說明:以上源碼查找在GitHub上有演示;