上一篇文章探究了对象的创建已经底层结构,这篇详细介绍isa、对象以及互相的关系。
isa是什么
从源码分析,isa是个共用体,封装了类的信息。
- nonpointer:是否对isa指针开启指针优化,0为纯isa指针,1:不止对象地址,还包含了类信息、对象引用计数等
- has_assoc:关联对象标志位
- has_cxx_dtor: 是否有c++析构函数xin
- shiftcls:存储类指针的值
- magic:用于调试器判断当前对象是否是真的对象还是没有初始化的空间
- weakly_referenced:是否指向或曾经指向弱引用
- unused: 未使用的
- has_sidetable_rc: 引用计数表
- extra_rc:引用计数大于10的时候
isa与类的关系
我们打印了实例对象和类对象的地址,然后通过控制台调试,验证了官网的图。0x0000000ffffffff8ULL为掩码,x/4gx 打印对象,是根据内存偏移,第一个是isa,会在下面的类结构中介绍。
对象的结构
万物皆对象。对象的本质是一个结构体,继承自objc_object(内部包含了isa),包含了superclass、cache、bits,其中cache_t,我们将在下一篇文章单独介绍。
bits内包含了class_rw_t和class_ro_t
class_rw_t结构为,内部包含了方法、属性、协议
method_array_t是一个二维数组
元素为method_t,包含了sel 、types和imp(sel相当于一本书的目录,imp目录所指的页码也就是具体实现)
同理property_array_t也是一样,内部是一个二维数组,含有property_t
protocol_array_t内部含有protocol_t
class_ro_t内部包含i属性、成员变量、协议。
实例对象、类对象、元类对象分别含有什么
这里就直接给出结论了,如果要验证的话,可以通过runtime去验证,大致思路为getIvarList获取成员变量,获取method,判断是否有地址(存在则有)。
- 实例对象内含有成员变量的具体值
- 对象方法、属性、成员变量、协议信息,存放在类对象中
- 类方法,存放在元类对象中
拓展
rw和ro一道面试题,问这样添加会不会有问题。
Class FFCar = objc_allocateClassPair([NSObject class], "FFCar", 0);
// class_addIvar(FFCar, "brand", sizeof(NSString *), log2(sizeof(NSString *)), "@");
objc_registerClassPair(FFCar);
class_addIvar(FFCar, "brand", sizeof(NSString *), log2(sizeof(NSString *)), "@");
id car = [FFCar alloc];
[car setValue:@"bgm" forKey:@"brand"];
FFLog(@"%@",[car valueForKey:@"brand"]);
答案是会崩溃,ivar存在ivars中,存在ro中,所以要在注册类之前就要添加进去。从源码就能看出来,RW_CONSTRUCTING属性是注册类的时候已经确定下来了。
void objc_registerClassPair(Class cls)
{
// Clear "under construction" bit, set "done constructing" bit
cls->ISA()->changeInfo(RW_CONSTRUCTED, RW_CONSTRUCTING | RW_REALIZING);
cls->changeInfo(RW_CONSTRUCTED, RW_CONSTRUCTING | RW_REALIZING);
}
BOOL
class_addIvar(Class cls, const char *name, size_t size,
uint8_t alignment, const char *type)
{
// Can only add ivars to in-construction classes.
if (!(cls->data()->flags & RW_CONSTRUCTING)) {
return NO;
}
}