目录
一,对象的三种类型
二,对象的存储信息
三,isa指针
四,superclass指针
五,isa和superclass总结
一,对象的三种类型
1,instance
对象:每次alloc都会产生新的
2,class
对象:每个类有且只有一个
3,meta-class
对象:每个类有且只有一个
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
的返回值是由参数来定的,传instance
对象返回class
对象,传class
对象返回meta-class
对象
二,对象的存储信息
存储信息1,实例对象的存储信息
@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
转为C++代码,Person
的底层代码如下,可以看到实例对象只包含isa
指针和成员变量
struct NSObject_IMPL {
Class isa;
};
struct Person_IMPL {
struct NSObject_IMPL NSObject_IVARS;
NSString *_name;
NSInteger _age; // 属性的本质是成员变量
};
2,类对象和元类对象的存储信息(源码下载地址)
- 它们的类型都是
Class
,Class
是指向objc_class
结构体的指针,下面是objc_class
结构体部分实现代码
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; // 协议列表
}
struct class_ro_t {
const ivar_list_t *ivars; // 成员变量列表
}
- 运行下面代码来验证一下,
Class
看不到具体的信息,所以将Class
转换为yj_objc_class
,yj_objc_class
就是objc_class
,只是把objc_class
代码copy出来加了个前缀而已,为了防止命名冲突
int main(int argc, char * argv[]) {
@autoreleasepool {
// 类对象
yj_objc_class *personClass = (__bridge yj_objc_class *)([Person class]);
class_rw_t *personClassData = personClass->data();
// 元类对象
yj_objc_class *personMetaClass = (__bridge yj_objc_class *)object_getClass([Person class]);
class_rw_t *personMetaClassData = personMetaClass->data();
}
return 0;
}
类对象
元类对象
-
总结:类对象和元类对象的结构是一样的,只是存放的数据不一样,类对象的
methods
里存放是实例方法,元类对象的methods
里存放是类方法,元类对象的ivars
,properties
,protocols
都为NULL -
注意:实例对象和类对象存储的成员变量是不同的,类对象存储的是成员变量的类型(
NSInteger
),名称(age
)等信息,而实例对象存储的是具体值
三,isa指针
1,指向
- 实例对象的
isa
指针指向类对象 - 类对象的
isa
指针指向元类对象
2,代码验证
NSObject *instance = [[NSObject alloc] init];
yj_objc_class *clas = (__bridge yj_objc_class *)[NSObject class];
Class metaClass = object_getClass([NSObject class]);
由下可知,isa
指针中保存的地址进行一次位运算就是对象的地址
3,作用
- 当执行
[p eat]
时,先通过对象p的isa
指针找到Person的类对象,然后在Person的类对象中找到eat
方法进行调用 - 当执行
[Person run]
时,先通过Person类对象的isa
指针找到Person的元类对象,然后在Person的元类对象中找到run
方法进行调用
Person *p = [[Person alloc] init];
[p eat];
[Person run];
四,superclass指针
1,指向
- 类对象的
superclass
指针指向父类的类对象 - 元类对象的
superclass
指针指向父类的元类对象
2,代码验证
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
指针中保存的地址就是对象的地址
3,作用
- 当执行
[s eat]
时,先通过对象s的isa
指针找到Student的类对象,然后通过Student类对象的superclass
指针找到Person的类对象,在Person的类对象中找到eat
方法进行调用 - 当执行
[Student run]
时,先通过Student类对象的isa
指针找到Student的元类对象,然后通过Student元类对象的superclass
指针找到Person的元类对象,在Person的元类对象中找到run
方法进行调用
Student *s = [[Student alloc] init];
[s eat];
[Student run];
五,isa和superclass总结
1,查找路径
-
[s copy]
的查找路径为下图中的红色箭头 -
[Student load]
的查找路径为下图中的蓝色箭头
// copy是NSObject的实例方法,load是NSObject的类方法
Student *s = [[Student alloc] init];
[s copy];
[Student load];
查找路径
2,特殊点
- 图中数字1和2:任何类的元类对象的
isa
指针都指向根类的元类对象 - 图中数字3:根类的元类对象的
isa
指针指向自己 - 图中数字4:根类的类对象的
superclass
指针指向nil - 图中数字5:根类的元类对象的
superclass
指针指向根类的类对象
3,验证数字5
问题:当+
方法没有实现时为何会调用-
方法?
答:因为方法调用最终会转换为objc_msgSend
进行执行,而objc_msgSend
是不区分+
和-
的,只根据方法名进行查找,[Student test]
会转换为objc_msgSend([Student class], @selector(test))
@interface NSObject (Test)
+ (void)test;
@end
@implementation NSObject (Test)
- (void)test {
NSLog(@"-test");
}
@end
[Student test]; // 下图是此代码的查找路径
// 打印
-test
验证数字5
网友评论