总纲领: OC底层探寻
我们来分析类的结构: 先引入一个列子,(注意以下代码是在objc源码中调试的):
例子说明: CJLPerson继承于NSObject, CJLTeacher继承于CJLPerson
int main(int argc, const char * argv[]) { @autoreleasepool { //ISA_MASK 0x00007ffffffffff8ULL CJLPerson *person = [CJLPerson alloc]; CJLTeacher *teacher = [CJLTeacher alloc]; NSLog(@"Hello, World! %@ - %@",person,teacher); } return 0; }
以下截图引用外部
lldb调试过程 上
lldb调试过程 中
lldb调试过程 下
调试代码有点长, 但是最终我们所得到的结果, 就是非常经典的一幅图:
类的继承以及类的isa指针总结图
类的结构分析
下面我们来一段objc_class的源码:
struct objc_class : objc_object { // Class ISA; //8字节 Class superclass; //Class是由objc_object定义的, 是一个指针, 8字节 cache_t cache; // formerly cache pointer and vtable //结构体要具体分析里面的属性, 分析出来也是8字节 class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags class_rw_t *data() const { return bits.data(); } void setData(class_rw_t *newData) { bits.setData(newData); } void setInfo(uint32_t set) { ASSERT(isFuture() || isRealized()); data()->setFlags(set); } void clearInfo(uint32_t clear) { ASSERT(isFuture() || isRealized()); data()->clearFlags(clear); } // set and clear must not overlap void changeInfo(uint32_t set, uint32_t clear) { ASSERT(isFuture() || isRealized()); ASSERT((set & clear) == 0); data()->changeFlags(set, clear); }
从上面代码我们可以看到 objc_class
继承与 objc_object
而objc_object
的源码为:
/// Represents an instance of a class. struct objc_object { Class _Nonnull isa OBJC_ISA_AVAILABILITY; };
我们可以看到objc_object
是一个结构体, 并且有一个isa属性, 而objc_class
继承于 objc_objt
, 所以objc_class
也有isa属性.
我们可以总结一下:
- 所有的对象 , 类 都有isa属性.
- objc_class继承于objc_object, 可以总结为万物皆对象.
要对类的结构分析, 这里要引入一个概念--内存偏移
//数组指针 int c[4] = {1, 2, 3, 4}; int *d = c; NSLog(@"%p -- %p - %p", &c, &c[0], &c[1]); NSLog(@"%p -- %p - %p", d, d+1, d+2);
打印结果:
打印结果.png
我们可以通过内存偏移来分析objc_class, 接刚开始objc_class的源码, 我们来分析cache:
struct cache_t { #if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_OUTLINED explicit_atomic<struct bucket_t *> _buckets; //*指针, 占8字节 explicit_atomic<mask_t> _mask; //mask_t 是unsigned int 的别名, 占4字节 #elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16 explicit_atomic<uintptr_t> _maskAndBuckets; //uintptr_t 是一个指针, 8字节 mask_t _mask_unused; //mask_t 是unsigned int 的别名, 占4字节 // How much the mask is shifted by. static constexpr uintptr_t maskShift = 48; //static 静态类型不存储在结构体中, 所以不算是结构体的内存 // Additional bits after the mask which must be zero. msgSend // takes advantage of these additional bits to construct the value // `mask << 4` from `_maskAndBuckets` in a single instruction. static constexpr uintptr_t maskZeroBits = 4; // The largest mask value we can store. static constexpr uintptr_t maxMask = ((uintptr_t)1 << (64 - maskShift)) - 1; // The mask applied to `_maskAndBuckets` to retrieve the buckets pointer. static constexpr uintptr_t bucketsMask = ((uintptr_t)1 << (maskShift - maskZeroBits)) - 1; // Ensure we have enough bits for the buckets pointer. static_assert(bucketsMask >= MACH_VM_MAX_ADDRESS, "Bucket field doesn't have enough bits for arbitrary pointers."); #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; static constexpr uintptr_t maskBits = 4; static constexpr uintptr_t maskMask = (1 << maskBits) - 1; static constexpr uintptr_t bucketsMask = ~maskMask; #else #error Unknown cache mask storage type. #endif #if __LP64__ uint16_t _flags; //uint16_t 是unsigned short的别名 占2个字节 #endif uint16_t _occupied; //uint16_t 是unsigned short的别名 占2个字节
通过上面的分析, 我们可以分析出不管走哪个路径, cache_t 一共占 8 + 4 + 2 + 2 = 16字节, 所以要想知道bits中的内容, 只需通过类的首地址, 然后平移32字节就可以得到了:
打印结果
总结
- 通过
{}
定义的成员变量
, 会存储在类的bits属性
中, 路径为bits --> data() --> ro() --> ivars
获取成员变量列表, 除了包括成员变量, 还包括属性定义的成员变量. - 通过
@property
定义的属性, 也会存储在类的bits属性
中, 路径为bits --> data() --> properties() --> list
获取属性列表, 其中只包含属性. - 类的
实例方法
存储在类的bits属性
中,路径为bits --> methods() --> list
获取实例方法列表, 其中除了包括实例方法, 还包括属性的set方法和get方法
. - 类的
类方法
存储在元类的bits属性中
, 路径为元类的bits --> methods() --> list
获取类方法列表.