iOS底层探索004-类分析
2020-09-16 本文已影响0人
星星1024
1. 类的分析
主要分析两个部分:isa的走向和继承关系
isa分析
类的isa走向,参考这篇文章
关系图
总结得出:
-
对象的isa指向类 -
类的isa指向元类 -
元类的isa指向根元类,即NSObject -
根元类的isa指向它自己
NSObject和根元类是否是一个?
验证1:
验证2:
Class class1 = [Person class];
Class class2 = [Person alloc].class;
Class class3 = object_getClass([Person alloc]);
NSLog(@"\n%p \n%p \n%p ", class1, class2, class3);
打印结果:
0x10d92d558
0x10d92d558
0x10d92d558
小结: 从结果中看出, 打印的地址都是同一个,所以NSObject只有一份,即NSObject(根元类)在内存中永远只存在一份*
objc_class & objc_object
通过上面分析我们发现类和对象都有isa, Why?
下面我们看下关于objc_class和objc_object的定义:
struct NSObject_IMPL {
Class isa;
};
typedef struct objc_class *Class;
struct objc_class {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class _Nullable super_class OBJC2_UNAVAILABLE;
const char * _Nonnull name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list * _Nullable ivars OBJC2_UNAVAILABLE;
struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE;
struct objc_cache * _Nonnull cache OBJC2_UNAVAILABLE;
struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE; //被废除的
/* Use `Class` instead of `struct objc_class *` */
我们发现在新的源码中被废弃了, 在781源码中看最新的
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() 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);
}
-
NSObject的底层编译是NSObject_IMPL结构体 -
Class是isa的指针类型, 由objc_class定义的类型 objc_class是一个结构体。在iOS中,所有的Class都是以objc_class为模板创建的- 新版本可知:
objc_class结构体类型是继承自objc_object的 -
objc_object是一个结构体, 且有isa属性, 故objc_class也有isa属性 -
NSObject是由objc_class定义的,也有isa属性 -
实例对象objc是由NSObject初始化的,也有isa属性
**总结: **
-
objc_object与对象的关系是继承关系 -
对象都是由objc_object继承来的(万物皆对象-> 以objc_object或objc_class者为模板创建的对象,都有isa属性) -
对象,类,元类都有isa属性
面试题:
- 类存在几份?
- 由于类的信息在内存中永远只存在一份,所以
类对象只有一份(验证1和验证2)
objc_object与对象的关系
- 所有的对象都是以
objc_object为模板继承过来的 - 所有的对象是来自
NSObject(OC),但是真正到底层的是一个objc_object(C/C++)的结构体类型(都有isa)
2. 类的结构分析
类结构体的定义:
struct objc_class : objc_object {
// Class ISA; //继承自objc_object的isa,占8字节
Class superclass; //Class是由objc_object定义的,是一个指针,占8字节
// 从类型无法判断大小.是个结构体,大小由内部属性决定, 结构体指针才是8字节
cache_t cache; // formerly cache pointer and vtable
//只有首地址经过上面3个属性的内存大小总和的平移,才能获取到bits
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);
}
计算 cache 大小
通过上面定义我们只要知道了cache的大小,就可以根据内存偏移读取bits的信息
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;
// How much the mask is shifted by.
static constexpr uintptr_t maskShift = 48;
// 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;
#endif
uint16_t _occupied;
-
ifbucket_t *结构体指针8字节,mask_t是uint32_t类型4字节 -
else ifuintptr_t是unsigned long类型8字节,mask_t是uint32_t类型4字节 -
uint16_t _flags是unsigned short类型2字节 -
uint16_t _occupied是unsigned short类型2字节
cache大小: 8 + 4 + 2 + 2 = 16字节
获取bits信息
根据上面计算可知,获取bits信息,需要首地址平移8+8+16=32字节
获取首地址方法(两种)
获取首地址方法.png
获取bits首地址(平移32字节)
平移32,获取bits
属性信息
获取属性
- 通过打印我们发现
properties只存储了属性,而没有成员变量,那么成员变量存储到哪里了呢?
成员变量
成员变量.png
小结:
- 通过
{}定义的成员变量,会存储在类的bits属性中,通过bits --> data() -->ro() --> ivars获取成员变量列表 - 通过
@property定义的属性,也会存储在bits属性中,通过bits --> data() --> properties() --> list获取属性列表,其中只包含属性
方法信息
实例方法
实例方法.png
类方法
类方法.png
-
类的实例方法存储在类的bits属性中,通过bits --> methods() --> list获取实例方法列表 -
bits包含属性的set和get方法 - 类的
类方法存储在元类的bits属性中, 通过bits --> methods() --> list获取类方法列表