Objective-C RunTime 学习笔记 之 基础结构体
1、OC 运行期常用对象结构体
union isa_t { Class cls; uintptr_t bits; /* unsigned long 无符号长整形,8个字节 */ #if SUPPORT_NONPOINTER_ISA # if __arm64__ struct { uintptr_t indexed : 1; uintptr_t has_assoc : 1; uintptr_t has_cxx_dtor : 1; uintptr_t shiftcls : 33; // MACH_VM_MAX_ADDRESS 0x1000000000 uintptr_t magic : 6; uintptr_t weakly_referenced : 1; uintptr_t deallocating : 1; uintptr_t has_sidetable_rc : 1; uintptr_t extra_rc : 19; }; # elif __x86_64__ struct { uintptr_t indexed : 1; uintptr_t has_assoc : 1; uintptr_t has_cxx_dtor : 1; uintptr_t shiftcls : 44; // MACH_VM_MAX_ADDRESS 0x7fffffe00000 uintptr_t magic : 6; uintptr_t weakly_referenced : 1; uintptr_t deallocating : 1; uintptr_t has_sidetable_rc : 1; uintptr_t extra_rc : 8; }; # else # error unknown architecture # endif #endif };
当64位下,使用 extra_rc 来存储引用计数,如果超出则同时将extra_rc的一半移到SideTable中。
1.3) objc_object/objc_class 结构体定义
/* Foundation 对象 */ struct objc_object { private: isa_t isa; // 仅仅定义了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 /* ...省略各种方法 */ }
可以看到无论是类还是对象,第一个字段为 isa_t ,在实例对象中指向类(存放实例变量内存布局、方法列表、属性列表、协议列表),而类则指向元类(类的方法列表、属性列表、协议列表)。class中有一个关键的结构体 class_data_bits_t
1.4) class_data_bits_t 结构体定义
struct class_data_bits_t { // Values are the FAST_ flags above. uintptr_t bits; /* 不同的位域存储不同的FLAG 和 class_rw_t 结构体的指针 */ /* 省略各种方法 */ }
在bits字段中,除了指向一个运行期的结构体 class_rw_t, 还在低4位标注了一些标识
1.5) class_rw_t/class_ro_t 结构体定义
struct class_rw_t { uint32_t flags; /* 定义flag集合 */ uint32_t version; /* 类的版本,貌似元类为7,普通类为0 */ const class_ro_t *ro; /* 这个结构体存放的编译期实例变量的布局以及实例变量的size、方法列表、属性列表、协议列表 */ method_array_t methods; /* 实例方法数组 */ property_array_t properties; /* 实例属性数组 */ protocol_array_t protocols; /* 协议数组 */ Class firstSubclass; Class nextSiblingClass; char *demangledName; /* 类名 */
/* 省略各种方法 */ } struct class_ro_t { uint32_t flags; uint32_t instanceStart; /* 实例变量开始的偏移量 */ uint32_t instanceSize; /* NSObject 类需要分配的字节数 */ #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; /* 本次属性列表 */ /* 省略各种方法 */ }
这个结构体 class_rw_t 中flags存放着运行期的一系列标识位,class_ro_t 是在编译期确定的结构,里面存放着实例变量的内存布局,这也是为什么在类定义好,不能在运行期加入实例变量的原因(通过 objc_allocateClassPair 除外,但是当调用 objc_registerClassPair 后也不允许加入实例变量),通过clang -rewrite-objc 可以看到大量的class_ro_t结构体定义,或者通过otool工具也可以查看编译后的 class_ro_t 结构。
class_rw_t 中 methods存放了所有本次定义的方法以及分类的方法,分类的方法加入到了数组的最前端,这也是为什么分类方法会覆盖掉类原始定义的方法,因为在通过LC_XXX命令加载镜像时,_read_images 会先加载类定义的方法,然后在加载分类的方法。
实验: 使用 otool -o 输出 iOS app 段,可以明显看到 class_ro_t 定义
/* 使用otool -o 输出iOS app 中的段 */ Contents of (__DATA,__objc_classlist) section 014a8048 0x16a2200 isa 0x16a2214 superclass 0x16ba2b0 cache 0x0 vtable 0x0 data 0x14ace5c (struct class_ro_t *) flags 0x90 instanceStart 4 instanceSize 8 ivarLayout 0x0 name 0x1108c74 HNAHiHomePageControl baseMethods 0x14acdf8 (struct method_list_t *) entsize 12 count 4 name 0x1036ddc initWithFrame: types 0x11248c5 @24@0:4{CGRect={CGPoint=ff}{CGSize=ff}}8 imp 0xaa21 name 0x1036e6a drawRect: types 0x11248ee v24@0:4{CGRect={CGPoint=ff}{CGSize=ff}}8 imp 0xaa6d name 0x1036e74 dotCornerRadius types 0x1124917 f8@0:4 imp 0xacf1 name 0x1036e84 setDotCornerRadius: types 0x112491e v12@0:4f8 imp 0xad01 baseProtocols 0x0 ivars 0x14ace30 entsize 20 count 1 offset 0x168eda8 4 name 0x1036e98 _dotCornerRadius type 0x1124928 f alignment 2 size 4 weakIvarLayout 0x0 baseProperties 0x14ace4c entsize 8 count 1 name 0x117a230 dotCornerRadius attributes 0x117a240 Tf,N,V_dotCornerRadius Meta Class isa 0x0 superclass 0x16ba2c4 cache 0x0 vtable 0x0 data 0x14acdd0 (struct class_ro_t *) flags 0x91 RO_META instanceStart 20 instanceSize 20 ivarLayout 0x0 name 0x1108c74 HNAHiHomePageControl baseMethods 0x0 (struct method_list_t *) baseProtocols 0x0 ivars 0x0 weakIvarLayout 0x0 baseProperties 0x0
无论遇到什么困难、一往直前