OC 中类也是一个对象,它的指针也会指向它所属的类,这样的类就是元类(MetaClass)
.元类的定义和建立,都是由编译器自动完成的.面试
类在底层都会被编译为结构体,全部的结构体都“继承”了 NSObject 结构体的
Class isa
,因此全部的类都包含了 isa,isa 指针指向了其所属的类.实例对象的 isa 指向&归属于类对象,类对象的 isa 指向&归属它的元类.实例对象->类对象->元类缓存
上篇文章,咱们看到 alloc 出来的实例对象中,isa 位域 shiftcls 保存的就是类的指针值,咱们深刻看下,类的 isa 指针,都指向了哪里呢?markdown
//Code
@interface LGPerson : NSObject @property(nonatomic, copy)NSString *kc_name; - (void)setNB; @end @implementation LGPerson - (void)setNB{ } @end LGPerson *person = [LGPerson alloc]; // DeBug (lldb) x/4gx person // 实例对象 0x10182cc50: 0x001d8001000021c9 0x0000000000000000 0x10182cc60: 0x63756f54534e5b2d 0x6f6c6f4372614268 (lldb) p/x 0x001d8001000021c9 & 0x00007ffffffffff8ULL (unsigned long long) $67 = 0x00000001000021c8 (lldb) po 0x00000001000021c8 LGPerson // 类(类对象) (lldb) x/4gx 0x00000001000021c8 0x1000021c8: 0x00000001000021a0 0x0000000100334140 0x1000021d8: 0x000000010032e430 0x0000801000000000 (lldb) po 0x00000001000021a0 LGPerson // 元类 (lldb) x/4gx 0x00000001000021a0 0x1000021a0: 0x00000001003340f0 0x00000001003340f0 0x1000021b0: 0x0000000101831790 0x0004e03100000007 (lldb) po 0x00000001003340f0 NSObject // 根元类 (lldb) x/4gx 0x00000001003340f0 // 根元类的isa指向本身 0x1003340f0: 0x00000001003340f0 0x0000000100334140 0x100334100: 0x0000000100640570 0x0005e03100000007 (lldb) 复制代码
// Code
@interface LGTercher : LGPerson @end @implementation LGTercher @end // DeBug (lldb) x/4gx tercher // 实例对象的内存分布 0x101c2c910: 0x001d800100002179 0x0000000000000000 0x101c2c920: 0x50626154534e5b2d 0x65695672656b6369 (lldb) p/x 0x001d800100002179 & 0x00007ffffffffff8ULL (unsigned long long) $12 = 0x0000000100002178 (lldb) po 0x0000000100002178 // 实例对象的isa指向了类LGTercher LGTercher (lldb) x/4gx 0x0000000100002178 // 类LGTercher的内存分布 0x100002178: 0x0000000100002150 0x00000001000021c8 0x100002188: 0x000000010032e430 0x0000801000000000 (lldb) po 0x0000000100002150 // 类的isa指向了它的元类 LGTercher (lldb) x/4gx 0x0000000100002150 // 元类的内存分布 0x100002150: 0x00000001003340f0 0x00000001000021a0 0x100002160: 0x0000000101c36480 0x0003e03100000007 (lldb) po 0x00000001003340f0 // 元类的isa指向了根元类 NSObject (lldb) x/4gx 0x00000001003340f0 0x1003340f0: 0x00000001003340f0 0x0000000100334140 0x100334100: 0x0000000101c36870 0x0004e03100000007 (lldb) 复制代码
由 LGTercher 的调试结果,咱们看到,元类 LGTercher 的 isa 直接指向了根元类,并不根据继承关系指向 LGPerson.因此全部的元类都会直接指向根元类.app
经典的走向图(继承:实线,isa指向:虚线):函数
流程图探究oop
无中生有
面试题:类在内存中存在几份?ui
答案是一份.虽然类归属于元类,但类的信息在编译后只会存在一份atom
以前利用Clang编译获得的main.cpp文件,咱们能够看到,类编译后的结果都是结构体,而每一个结构体都会"继承"自NSObject_IMPL结构体,而Class类型表示了指向objc_class结构体的指针
。spa
struct NSObject_IMPL {
Class isa; }; typedef struct objc_class *Class; 复制代码
这时在cpp文件中,点不到objc_class结构体,咱们再来到781版本的objc源码
Source Browser全局搜一下。3d
在objc-runtime-new.h文件中的objc_class是最新的,而且它继承自objc_object
。
objc_object源码:
由objc_class的结构体源码,咱们能够经过类的首地址偏移,来获取到bits中的信息。
x/4gx LGPerson.class
0x100002250: 0x0000000100002228 0x0000000100334140 0x100002260: 0x000000010032e420 0x0000802400000000 复制代码
LGPerson类的首地址为0x100002250
ISA
和superclass
都是objc_class的结构体指针,一共16字节。// cache关键部分,static&方法()不在计算范围内
struct cache_t { #if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_OUTLINED explicit_atomic<struct bucket_t *> _buckets; explicit_atomic<mask_t> _mask; #elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16 explicit_atomic<uintptr_t> _maskAndBuckets; mask_t _mask_unused; #elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_LOW_4 // _maskAndBuckets stores the mask shift in the low 4 bits, and // the buckets pointer in the remainder of the value. The mask // shift is the value where (0xffff >> shift) produces the correct // mask. This is equal to 16 - log2(cache_size). explicit_atomic<uintptr_t> _maskAndBuckets; mask_t _mask_unused; ... #endif #if __LP64__ uint16_t _flags; #endif uint16_t _occupied; ... 复制代码
struct bucket_t *
类型是结构体指针,8字节typedef uint32_t mask_t;
类型是4字节typedef unsigned long uintptr_t;
类型是8字节uint16_t
类型是2字节 因此cache所占内存一共
12+2+2=16
字节,最终的偏移量为32字节
。
/* Debug 首地址为:0x100002250 偏移量为32字节, class_data_bits_t bits的地址就在0x100002270 (16进制) */ p (class_data_bits_t *)0x100002270 // (class_data_bits_t *)$3 = 0x00000000100002270 p $3->data() // 调用bits中的data()方法 //(class_rw_t *)$4 = 0x0000000100b19bf0 p $4 /* (class_rw_t) $6 = { flags = 2148007936 witness = 0 ro_or_rw_ext = { std::_ _1::atomic<unsigned long> = 4294975616 } firstSubclass = LGTercher nextSiblingClass = NSUUID } */ 复制代码
$6都是些啥玩意儿啊??!!
咱们点到bits.data()方法的返回类型class_rw_t
:
struct class_rw_t {
... const method_array_t methods() const { auto v = get_ro_or_rwe(); if (v.is<class_rw_ext_t *>()) { return v.get<class_rw_ext_t *>()->methods; } else { return method_array_t{v.get<const class_ro_t *>()->baseMethods()}; } } const property_array_t properties() const { auto v = get_ro_or_rwe(); if (v.is<class_rw_ext_t *>()) { return v.get<class_rw_ext_t *>()->properties; } else { return property_array_t{v.get<const class_ro_t *>()->baseProperties}; } } } 复制代码
一直看到最后,看到了method_array_t methods()
和property_array_t properties()
两个方法。
输出后咱们看到,property_list_t中的属性数据count
有一条,属性的名称为kc_name
,和LGPerson中是相符的。
$4
和上边$6
同样,都是class_rw_t
数据) 此时咱们能够看到,methods中一共有4个方法
(method_t) $9 = {
name = "sayNB" ... } (method_t) $10 = { name = "kc_name" ... } (method_t) $11 = { name = "setKc_name:" ... } (method_t) $12 = { name = ".cxx_destruct" ... } 复制代码
class_data_bits_t bits
中本文使用 mdnice 排版