Runtime是一个用C、C++、汇编编写的运行时库,包含了很多C语言的API,封装了很多动态性相关的函数;Objective-C是一门动态运行时语言,允许很多操作推迟到程序运行时再进行。OC的动态性就是由Runtime来支撑和实现的,Rumtime就是它的核心;我们平时编写的OC代码,底层都是转换成了Runtime API进行调用。
由此可见Runtime的重要性,这一篇主要讲下Runtime的数据结构,也为后续分析Runtime的其他特性提供基础。
isa 指针
struct objc_object {
Class isa; // 在 arm64 架构之前
};
struct objc_object {
private:
isa_t isa; // 在 arm64 架构开始
};
union isa_t
{
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
Class cls;
uintptr_t bits;
#if SUPPORT_PACKED_ISA
// extra_rc must be the MSB-most field (so it matches carry/overflow flags)
// nonpointer must be the LSB (fixme or get rid of it)
// shiftcls must occupy the same bits that a real class pointer would
// bits + RC_ONE is equivalent to extra_rc + 1
// RC_HALF is the high bit of extra_rc (i.e. half of its range)
// future expansion:
// uintptr_t fast_rr : 1; // no r/r overrides
// uintptr_t lock : 2; // lock for atomic property, @synch
// uintptr_t extraBytes : 1; // allocated with extra bytes
# if __arm64__
# define ISA_MASK 0x0000000ffffffff8ULL // 用来取出 Class、Meta-Class 对象的内存地址
# define ISA_MAGIC_MASK 0x000003f000000001ULL
# define ISA_MAGIC_VALUE 0x000001a000000001ULL
struct {
uintptr_t nonpointer : 1; // 0:代表普通的指针,存储着 Class、Meta-Class 对象的内存地址
// 1:代表优化过,使用位域存储更多的信息
uintptr_t has_assoc : 1; // 是否有设置过关联对象,如果没有,释放时会更快
uintptr_t has_cxx_dtor : 1; // 是否有C++的析构函数(.cxx_destruct),如果没有,释放时会更快
uintptr_t shiftcls : 33; // 存储着 Class、Meta-Class 对象的内存地址信息
uintptr_t magic : 6; // 用于在调试时分辨对象是否未完成初始化
uintptr_t weakly_referenced : 1; // 是否有被弱引用指向过,如果没有,释放时会更快
uintptr_t deallocating : 1; // 对象是否正在释放
uintptr_t has_sidetable_rc : 1; // 如果为1,代表引用计数过大无法存储在 isa 中,那么超出的引用计数会存储在一个叫 SideTable 结构体的 RefCountMap(引用计数表)哈希表中
uintptr_t extra_rc : 19; // 里面存储的值是引用计数 retainCount - 1
# define RC_ONE (1ULL<<45)
# define RC_HALF (1ULL<<18)
};
};
isa指针用来维护对象和类之间的关系,并确保对象和类能够通过isa指针找到对应的方法、实例变量、属性、协议等;在arm64架构之前,isa就是一个普通的指针,直接指向objc_class,存储着Class、Meta-Class对象的内存地址。instance对象的isa指向class对象,class对象的isa指向meta-class对象;从 arm64 架构开始,对isa进行了优化,变成了一个共用体(union)结构,不仅仅承担指针的作用,还使用位域来存储更多的信息。
isa与superclass指针指向
由上图可见
实例对象的isa指向类对象,类对象的isa指向元类对象;元类对象的isa指向根元类对象,根元类对象的isa指向自己类对象与元类对象的superclass指向各自的父类,特别注意的一点是根元类对象的superclass指向根类对象。
类对象(class)与元类对象(meta-class)
class、meta-class底层结构都是objc_class结构体,objc_class继承自objc_object,所以它也有isa指针,所以它也是对象;class中存储着实例方法、成员变量、属性、协议等信息,meta-class中存储着类方法等信息;isa指针和superclass指针的指向(如上图);- 基类的
meta-class的superclass指向基类的class,决定了一个性质:当我们调用一个类方法,会通过class的isa指针找到meta-class,在meta-class中查找有无该类方法,如果没有,再通过meta-class的superclass指针逐级查找父meta-class,一直找到基类的meta-class如果还没找到该类方法的话,就会去找基类的class中同名的实例方法的实现。
objc_object
Objective-C的面向对象都是基于C/C++的数据结构——结构体实现的。
我们平时使用的所有对象都是id类型,id类型对象对应到runtime中,就是objc_object结构体。
struct objc_object {
private:
isa_t isa;
/*...
isa操作相关
弱引用相关
关联对象相关
内存管理相关
...
*/
};
objc_class
Class指针用来指向一个 Objective-C 的类,它是objc_class结构体类型,所以class、meta-class底层结构都是objc_class结构体,objc_class继承自objc_object,所以它也有isa指针,它也是对象。
struct objc_class : objc_object {
// Class ISA;
Class superclass; // 指向父类
cache_t cache; // 方法缓存 formerly cache pointer and vtable
class_data_bits_t bits; // 用于获取具体的类信息 class_rw_t * plus custom rr/alloc flags
class_rw_t *data() {
return bits.data();
}
};
class_data_bits_t
class_data_bits_t主要是对class_rw_t的封装,可以通过bits & FAST_DATA_MASK获得class_rw_t。class_rw_t代表了类相关的读写信息,它是对class_ro_t的封装;class_rw_t中主要存储着类的方法列表、属性列表、协议列表等;class_rw_t里面的methods、properties、protocols都继承于list_array_tt二维数组,是可读可写的,包含了类的初始内容、分类的内容。
struct class_rw_t {
// Be warned that Symbolication knows the layout of this structure.
// 请注意,Symbolication 知道此结构的布局。
// 在上篇的测试代码中:flags 打印看到是 2148007936
// 转为二进制的话是只有 31 位和 19 位是 1,其它位全部都是 0,对应于:
// class has started realizing but not yet completed it
// #define RW_REALIZING (1<<19)
// class_t->data is class_rw_t, not class_ro_t
// #define RW_REALIZED (1<<31)
uint32_t flags;
//(控制台打印值为 1)
uint16_t witness;
#if SUPPORT_INDEXED_ISA // isa 中保存 indexcls,大概是 watchOS 下才会用到
uint16_t index;
#endif
// std::atomic<uintptr_t>
// 原子性 unsigned long
// 执行如下命令,会打印 error:
// (lldb) p $2->ro_or_rw_ext
// error: no member named 'ro_or_rw_ext' in 'class_rw_t'
// 在编译时会根据类定义生成类的 class_ro_t 数据,其中包含方法列表、属性列表、成员变量列表等等内容
// ro_or_rw_ext 会有两种情况:
// 1): 编译时值是 class_ro_t *
// 2): 编译后类实现完成后值是 class_rw_ext_t *,而编译时的 class_ro_t * 作为 class_rw_ext_t 的 const class_ro_t *ro 成员变量保存
explicit_atomic<uintptr_t> ro_or_rw_ext; // 变量名字对应与 class_ro_t or(或) class_rw_ext_t
// 当前所属类的第一个子类
// 测试时,定义了一个继承自 NSObject 的类,
// 控制台打印看到它的 firstSubclass 是 nil
Class firstSubclass;
// 姊妹类、兄弟类
// 测试时,定义了一个继承自 NSObject 的类,
// 控制台打印看到 nextSiblingClass 是 NSUUID(好奇怪)
// firstSubclass 和 nextSiblingClass 有超级重要的作用,后面会展开
Class nextSiblingClass;
...
};
class_ro_t代表了类相关的只读信息class_ro_t中主要存储着类的成员变量列表、类名等;class_ro_t里面的baseMethodList、baseProtocols、ivars、baseProperties是一维数组,是只读的,包含了类的初始内容;- 一开始类的信息都存放在
class_ro_t里,当程序运行时,经过一系列的函数调用栈,在realizeClass()函数中,将class_ro_t里的东西和分类的东西合并起来放到class_rw_t里,并让bits指向class_rw_t。
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize; // instance对象占用的内存空间
#ifdef __LP64__
uint32_t reserved;
#endif
const uint8_t * ivarLayout;
const char * name; // 类名
method_list_t * baseMethodList;
protocol_list_t * baseProtocols;
const ivar_list_t * ivars; // 成员变量列表
const uint8_t * weakIvarLayout;
property_list_t *baseProperties;
method_list_t *baseMethods() const {
return baseMethodList;
}
};
class_rw_ext_t只有在动态添加属性、方法或协议时创建- 与当前类存在类别
struct class_rw_ext_t {
DECLARE_AUTHED_PTR_TEMPLATE(class_ro_t)
class_ro_t_authed_ptr<const class_ro_t> ro;
method_array_t methods;
property_array_t properties;
protocol_array_t protocols;
char *demangledName;
uint32_t version;
};
method_array_t与method_list_t