目錄
一,對(duì)象的三種類型
二顷蟀,對(duì)象的存儲(chǔ)信息
三,isa指針
四趾痘,superclass指針
五,isa和superclass總結(jié)
一榛斯,對(duì)象的三種類型
1观游,instance
對(duì)象:每次alloc都會(huì)產(chǎn)生新的
2,class
對(duì)象:每個(gè)類有且只有一個(gè)
3驮俗,meta-class
對(duì)象:每個(gè)類有且只有一個(gè)
NSObject *instance1 = [[NSObject alloc] init];
NSObject *instance2 = [[NSObject alloc] init];
NSLog(@"instance---%p---%p", instance1, instance2);
Class class1 = [NSObject class];
Class class2 = [instance1 class];
Class class3 = object_getClass(instance2);
NSLog(@"class---%p---%p---%p", class1, class2, class3);
Class metaClass1 = object_getClass(class1);
Class metaClass2 = object_getClass(class2);
NSLog(@"metaClass---%p---%p", metaClass1, metaClass2);
// 打印
instance---0x600002b0c290---0x600002b0c2a0 // 地址不同
class---0x10c7b6ec8---0x10c7b6ec8---0x10c7b6ec8 // 地址相同
metaClass---0x10c7b6e78---0x10c7b6e78 // 地址相同
注意:object_getClass
的返回值是由參數(shù)來定的懂缕,傳instance
對(duì)象返回class
對(duì)象,傳class
對(duì)象返回meta-class
對(duì)象
二王凑,對(duì)象的存儲(chǔ)信息
1搪柑,實(shí)例對(duì)象的存儲(chǔ)信息
@interface Person : NSObject <NSCopying>
{
NSString *_name;
}
@property (nonatomic, assign) NSInteger age;
- (void)eat;
+ (void)run;
@end
@implementation Person
- (void)eat {
NSLog(@"eat");
}
+ (void)run {
NSLog(@"run");
}
@end
將上述代碼用clang
轉(zhuǎn)為C++代碼,Person
的底層代碼如下索烹,可以看到實(shí)例對(duì)象只包含isa
指針和成員變量
struct NSObject_IMPL {
Class isa;
};
struct Person_IMPL {
struct NSObject_IMPL NSObject_IVARS;
NSString *_name;
NSInteger _age; // 屬性的本質(zhì)是成員變量
};
2工碾,類對(duì)象和元類對(duì)象的存儲(chǔ)信息(源碼下載地址)
- 它們的類型都是
Class
,Class
是指向objc_class
結(jié)構(gòu)體的指針百姓,下面是objc_class
結(jié)構(gòu)體部分實(shí)現(xiàn)代碼
struct objc_object {
isa_t isa;
}
struct objc_class : objc_object {
Class superclass;
class_rw_t *data() {
return bits.data();
}
}
struct class_rw_t {
const class_ro_t *ro;
method_array_t methods; // 方法列表
property_array_t properties; // 屬性列表
protocol_array_t protocols; // 協(xié)議列表
}
struct class_ro_t {
const ivar_list_t *ivars; // 成員變量列表
}
- 運(yùn)行下面代碼來驗(yàn)證一下渊额,
Class
看不到具體的信息,所以將Class
轉(zhuǎn)換為yj_objc_class
垒拢,yj_objc_class
就是objc_class
旬迹,只是把objc_class
代碼copy出來加了個(gè)前綴而已,為了防止命名沖突
int main(int argc, char * argv[]) {
@autoreleasepool {
// 類對(duì)象
yj_objc_class *personClass = (__bridge yj_objc_class *)([Person class]);
class_rw_t *personClassData = personClass->data();
// 元類對(duì)象
yj_objc_class *personMetaClass = (__bridge yj_objc_class *)object_getClass([Person class]);
class_rw_t *personMetaClassData = personMetaClass->data();
}
return 0;
}
總結(jié):類對(duì)象和元類對(duì)象的結(jié)構(gòu)是一樣的求类,只是存放的數(shù)據(jù)不一樣奔垦,類對(duì)象的
methods
里存放是實(shí)例方法,元類對(duì)象的methods
里存放是類方法尸疆,元類對(duì)象的ivars
椿猎,properties
,protocols
都為NULL注意:實(shí)例對(duì)象和類對(duì)象存儲(chǔ)的成員變量是不同的仓技,類對(duì)象存儲(chǔ)的是成員變量的類型(
NSInteger
)鸵贬,名稱(age
)等信息,而實(shí)例對(duì)象存儲(chǔ)的是具體值
三脖捻,isa指針
1阔逼,指向
- 實(shí)例對(duì)象的
isa
指針指向類對(duì)象 - 類對(duì)象的
isa
指針指向元類對(duì)象
2,代碼驗(yàn)證
NSObject *instance = [[NSObject alloc] init];
yj_objc_class *clas = (__bridge yj_objc_class *)[NSObject class];
Class metaClass = object_getClass([NSObject class]);
由下可知地沮,isa
指針中保存的地址進(jìn)行一次位運(yùn)算就是對(duì)象的地址
3嗜浮,作用
- 當(dāng)執(zhí)行
[p eat]
時(shí),先通過對(duì)象p的isa
指針找到Person的類對(duì)象摩疑,然后在Person的類對(duì)象中找到eat
方法進(jìn)行調(diào)用 - 當(dāng)執(zhí)行
[Person run]
時(shí)危融,先通過Person類對(duì)象的isa
指針找到Person的元類對(duì)象,然后在Person的元類對(duì)象中找到run
方法進(jìn)行調(diào)用
Person *p = [[Person alloc] init];
[p eat];
[Person run];
四雷袋,superclass指針
1吉殃,指向
- 類對(duì)象的
superclass
指針指向父類的類對(duì)象 - 元類對(duì)象的
superclass
指針指向父類的元類對(duì)象
2,代碼驗(yàn)證
yj_objc_class *studentClass = (__bridge yj_objc_class *)[Student class];
yj_objc_class *personClass = (__bridge yj_objc_class *)[Person class];
Class objectClass = [NSObject class];
由下可知,superclass
指針中保存的地址就是對(duì)象的地址
3蛋勺,作用
- 當(dāng)執(zhí)行
[s eat]
時(shí)瓦灶,先通過對(duì)象s的isa
指針找到Student的類對(duì)象,然后通過Student類對(duì)象的superclass
指針找到Person的類對(duì)象抱完,在Person的類對(duì)象中找到eat
方法進(jìn)行調(diào)用 - 當(dāng)執(zhí)行
[Student run]
時(shí)贼陶,先通過Student類對(duì)象的isa
指針找到Student的元類對(duì)象,然后通過Student元類對(duì)象的superclass
指針找到Person的元類對(duì)象巧娱,在Person的元類對(duì)象中找到run
方法進(jìn)行調(diào)用
Student *s = [[Student alloc] init];
[s eat];
[Student run];
五碉怔,isa和superclass總結(jié)
1,查找路徑
-
[s copy]
的查找路徑為下圖中的紅色箭頭 -
[Student load]
的查找路徑為下圖中的藍(lán)色箭頭
// copy是NSObject的實(shí)例方法禁添,load是NSObject的類方法
Student *s = [[Student alloc] init];
[s copy];
[Student load];
2撮胧,特殊點(diǎn)
- 圖中數(shù)字1和2:任何類的元類對(duì)象的
isa
指針都指向根類的元類對(duì)象 - 圖中數(shù)字3:根類的元類對(duì)象的
isa
指針指向自己 - 圖中數(shù)字4:根類的類對(duì)象的
superclass
指針指向nil - 圖中數(shù)字5:根類的元類對(duì)象的
superclass
指針指向根類的類對(duì)象
3,驗(yàn)證數(shù)字5
問題:當(dāng)+
方法沒有實(shí)現(xiàn)時(shí)為何會(huì)調(diào)用-
方法上荡?
答:因?yàn)榉椒ㄕ{(diào)用最終會(huì)轉(zhuǎn)換為objc_msgSend
進(jìn)行執(zhí)行趴樱,而objc_msgSend
是不區(qū)分+
和-
的,只根據(jù)方法名進(jìn)行查找酪捡,[Student test]
會(huì)轉(zhuǎn)換為objc_msgSend([Student class], @selector(test))
@interface NSObject (Test)
+ (void)test;
@end
@implementation NSObject (Test)
- (void)test {
NSLog(@"-test");
}
@end
[Student test]; // 下圖是此代碼的查找路徑
// 打印
-test