面試題引發的思考:
Q: OC的類信息存放在哪里?
- 類方法,存放在meta-class對象中;
- 對象方法、屬性、成員變量、協議信息,存放在class對象中;
- 成員變量的具體值,存放在instance對象。
Q: 對象的isa
指針指向哪里?
- instance對象的
isa
指向class對象; - class對象的
isa
指向meta-class對象; - meta-class對象的
isa
指向基類的meta-class對象。
1. OC的類信息存放在哪里?
(1) OC對象的分類:
Objective-C對象,簡稱OC對象,主要分為3種:
- instance對象(實例對象)
- class對象(類對象)
- meta-class對象(元類對象)
1> instance對象(實例對象)
instance對象就是通過類alloc
出來的對象,每次調用alloc
都會產生新的instance對象。
int main(int argc, const char * argv[]) {
@autoreleasepool {
// instance對象,實例對象
NSObject *object1 = [[NSObject alloc] init];
NSObject *object2 = [[NSObject alloc] init];
}
return 0;
}
-
object1
、object2
是NSObject
的instance對象(實例對象); - 它們是不同的兩個對象,分別占據著兩塊不同的內存。
instance對象在內存中存儲的信息包括:
isa
指針- 其他成員變量
@interface Person : NSObject {
@public
int _age;
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *p1 = [[Person alloc] init];
p1->_age = 3;
Person *p2 = [[Person alloc] init];
p2->_age = 4;
}
return 0;
}
2> class對象(類對象)
通過class
方法或runtime方法可以得到class對象:
int main(int argc, const char * argv[]) {
@autoreleasepool {
// instance對象,實例對象
NSObject *object1 = [[NSObject alloc] init];
NSObject *object2 = [[NSObject alloc] init];
// class對象,類對象
// class方法返回的一直是class對象,類對象
Class objectClass1 = [object1 class];
Class objectClass2 = [object2 class];
Class objectClass3 = [NSObject class];
// runtime
Class objectClass4 = object_getClass(object1);
Class objectClass5 = object_getClass(object2);
// 以下輸出地址相同
NSLog(@"%p %p %p %p %p", objectClass1, objectClass2, objectClass3, objectClass4, objectClass5);
}
return 0;
}
-
objectClass1
~objectClass5
都是NSObject
的class對象; - 它們是同一個對象,每個類在內存中有且只有一個class對象。
class對象在內存中存儲的信息主要包括:
isa
指針superclass
指針- 類的屬性信息(
property
),類的成員變量信息(ivar
)- 類的對象方法信息(
instance method
),類的協議信息(protocol
)- ......
3> meta-class對象(元類對象)
int main(int argc, const char * argv[]) {
@autoreleasepool {
// meta-class對象,元類對象
// 將類對象當做參數傳入,獲得元類對象
Class objectMetaClass = object_getClass([NSObject class]);
// 而調用類對象的class方法時得到還是類對象,無論調用多少次都是類對象
Class objectMetaClass2 = [[[NSObject class] class] class];
// 判斷該對象是否為元類對象
BOOL result = class_isMetaClass([NSObject class]);
}
return 0;
}
-
objectMetaClass
是NSObject
的meta-class對象; - 每個類在內存中有且只有一個meta-class對象。
meta-class對象在內存中存儲的信息主要包括:
isa
指針superclass
指針- 類的類方法信息(
class method
)- ......
- meta-class對象和class對象的內存結構是一樣的,但是用途不一樣:
- meta-class對象主要存放:類方法
- class對象主要存放:類的屬性,類的對象方法、協議、成員變量信息等
- instance對象主要存放:成員變量
補充:objc_getClass
、object_getClass
、class
區別:
1. Class objc_getClass(const char *aClassName)
1> 傳入字符串類名
2> 返回對應的類對象
2. Class object_getClass(id obj)
1> 傳入的obj可能是instance對象、class對象、meta-class對象
2> 返回值
a) 如果是instance對象,返回class對象
b) 如果是class對象,返回meta-class對象
c) 如果是meta-class對象,返回NSObject(基類)的meta-class對象
3. - (Class)class、+ (Class)class
1> 返回的就是類對象
2. 對象的isa
指針指向哪里?
(1) isa
指針、superclass
指針:
Q: instance對象、class對象、meta-class對象中的isa
指針,有何作用?
Q: class對象、meta-class對象中的superclass
指針又有何作用?
如上圖所示:
- instance的
isa
指向class:
當調用對象方法時,通過instance的isa
找到class,最后找到對象方法的實現進行調用;- class的
isa
指向meta-class:
當調用類方法時,通過class的isa
找到meta-class,最后找到類方法的實現進行調用。
1> 對象方法的實現流程
如上圖所示, 當Student
的instance對象要調用Person
的對象方法時:
- 首先通過
isa
找到Student
的class; - 然后通過
superclass
找到Person
的class; - 最后找到對象方法的實現進行調用。
2> 類方法的實現流程
如上圖所示,當Student
的class對象要調用Person
的類方法時:
- 首先通過
isa
找到Student
的meta-class; - 然后通過
superclass
找到Person
的meta-class; - 最后找到類方法的實現進行調用。
(2) 下圖詳盡的顯示出isa
指針、superclass
指針的指向:
總結可知:
- instance的
isa
指向class- class的
isa
指向meta-class- meta-class的
isa
指向基類的meta-class- class的
superclass
指向父類的class
如果沒有父類,superclass
指針為nil
- meta-class的
superclass
指向父類的meta-class
基類的meta-class的superclass
指向基類的class- instance調用對象方法的軌跡
isa
找到class,方法不存在,就通過superclass
找父類- class調用類方法的軌跡
isa
找meta-class,方法不存在,就通過superclass
找父類
(3) 驗證:
1> 首先驗證object
的isa
指針是否指向objectClass
:
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSObject *object = [[NSObject alloc] init];
Class objectClass = [NSObject class];
Class objectMetaClass = object_getClass(objectClass);
NSLog(@"%p %p %p", object, objectClass, objectMetaClass);
}
return 0;
}
通過打斷點打印相應對象的isa
指針:
Q: 為什么object
的isa
指針和objectClass
的地址不同呢?
查看OC源碼發現:
從64bit開始,
isa
需要進行一次位運算,才能計算出真實地址
那么進行位運算:
object
的isa
指針地址0x001dffff96537141
與0x00007ffffffffff8
進行位運算,得到0x00007fff96537140
,與objectClass
的地址相同;驗證完成。
2> 再驗證objectClass
的isa
指針是否指向objectMetaClass
:
通過打印objectClass->isa
:
無法獲得結果,根據提示進入Class內部查看:
objc_class
結構體內部第一個對象是isa
指針,為了獲取isa
指針,我們通過創建一個相同結構的結構體并通過強制轉化拿到isa
指針:
struct my_object_class {
Class isa;
};
int main(int argc, const char * argv[]) {
@autoreleasepool {
Class objectClass = [NSObject class];
Class objectMetaClass = object_getClass(objectClass);
struct my_object_class *objectClass2 = (__bridge struct my_object_class *)(objectClass);
}
return 0;
}
那么進行位運算:
objectClass
的isa
指針地址0x001dffff8b42d0f1
與0x00007ffffffffff8
進行位運算,得到0x001dffff8b42d0f0
,與meta-class的地址相同;驗證完成。