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;
-
if
bucket_t *
结构体指针8
字节,mask_t
是uint32_t
类型4
字节 -
else if
uintptr_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字节)
属性信息
获取属性- 通过打印我们发现
properties
只存储了属性
,而没有成员变量
,那么成员变量存储到哪里了呢?
成员变量
成员变量.png小结:
- 通过
{}
定义的成员变量
,会存储在类
的bits
属性中,通过bits --> data() -->ro() --> ivars
获取成员变量列表
- 通过
@property
定义的属性
,也会存储在bits
属性中,通过bits --> data() --> properties() --> list
获取属性列表
,其中只包含属性
方法信息
实例方法
类方法
-
类
的实例方法
存储在类的bits
属性中,通过bits --> methods() --> list
获取实例方法列表
-
bits
包含属性
的set
和get
方法 - 类的
类方法
存储在元类的bits属性
中, 通过bits --> methods() --> list
获取类方法列表
网友评论