- 001-OC对象原理探究 - alloc这篇文章的探索过程中,大体上讲述了alloc的基本流程。其中探索到
_class_createInstanceFromZone方法中的下面这段代码时,简单讲了对象的关联。">引言
在001-OC对象原理探究 - alloc这篇文章的探索过程中,大体上讲述了alloc的基本流程。其中探索到_class_createInstanceFromZone方法中的下面这段代码时,简单讲了对象的关联。 - 002-OC对象原理探究 - 结构体内存对齐">isa的内部结构以及
nonpointer和TaggedPointer的解释
isa结构源码如下,是一个联合体+位域的结构:
联合体union内部成员为互斥存在,即联合体所占内存大小决定于内部最大成员所占大小。结构体struct则是“有容乃大”,占内存大小可参考002-OC对象原理探究 - 结构体内存对齐- 这篇文章
####nonpointer
在ISA_BITFIELD内部,可看到nonpointer占1位,表示是否对 isa 指针开启指针优化(0:纯isa指针,1:不⽌是类对象地址,isa 中包含了类信息、对象的引⽤计数等)。应用判断,在上面已说明。">TaggedPointer
早期64位架构中,存储基础数据类型 , 底层会封装成NSNumber, 在堆开辟8字节内存,32位架构开辟4字节。维护引用计数,管理生命期 。造成运行效率上的损失 。会造成很大空间浪费。因此,引入TaggedPointer。当断言为TaggedPointer,则对象指针的值不是地址了,而是真正的值,直接优化了内存,提升了获取速度。TaggedPointer的更多知识,可阅读这篇文章
####nonpointer
在ISA_BITFIELD内部,可看到nonpointer占1位,表示是否对 isa 指针开启指针优化(0:纯isa指针,1:不⽌是类对象地址,isa 中包含了类信息、对象的引⽤计数等)。应用判断,在上面已说明。
- 这篇文章
- isa的用法
isa的用法和isa的走向将在下篇文章探究。
###总结
每个类都有一个isa,isa的探究重要性不言而喻。我们通过alloc流程探究到isa的内部结构,补上了001文章的alloc流程的一个坑。好累,拜拜。。
引言
在001-OC对象原理探究 - alloc这篇文章的探索过程中,大体上讲述了alloc的基本流程。其中探索到_class_createInstanceFromZone方法中的下面这段代码时,简单讲了对象的关联。
if (!zone && fast) {obj->initInstanceIsa(cls, hasCxxDtor);} else {obj->initIsa(cls);}
因此,本文将详细探究这个神秘的isa。
###initInstanceIsa和initIsa源码
我们在源码中,点击initInstanceIsa(cls,hasCxxDtor)进入到objc-object.h中可以看到,函数内部做了两个断言
ASSERT(!cls->instancesRequireRawIsa());ASSERT(hasCxxDtor == cls->hasCxxDtor());
1、instancesRequireRawIsa()内部为cache.getBit(FAST_CACHE_REQUIRES_RAW_ISA);。其中FAST_CACHE_REQUIRES_RAW_ISA为类的cache_t中的uint16_t _flags第13位是否为1,以此来表示类对象(非实例对象)是否需要原始的isa。
2、cls->hasCxxDtor()内部为cache.getBit(FAST_CACHE_HAS_CXX_DTOR);。其中FAST_CACHE_HAS_CXX_DTOR为类cache_t中uint16_t _flags第2位是否为1,以此来表示当前类或者父类是否有c++的析构函数的实现。
接下来就是进入到initIsa(cls, true, hasCxxDtor);内部源码如下:
inline voidobjc_object::initIsa(Class cls, bool nonpointer, UNUSED_WITHOUT_INDEXED_ISA_AND_DTOR_BIT bool hasCxxDtor){ASSERT(!isTaggedPointer());isa_t newisa(0);if (!nonpointer) {newisa.setClass(cls, this);} else {ASSERT(!DisableNonpointerIsa);ASSERT(!cls->instancesRequireRawIsa());#if SUPPORT_INDEXED_ISAASSERT(cls->classArrayIndex() > 0);newisa.bits = ISA_INDEX_MAGIC_VALUE;newisa.has_cxx_dtor = hasCxxDtor;newisa.indexcls = (uintptr_t)cls->classArrayIndex();#elsenewisa.bits = ISA_MAGIC_VALUE;# if ISA_HAS_CXX_DTOR_BITnewisa.has_cxx_dtor = hasCxxDtor;# endifnewisa.setClass(cls, this);#endifnewisa.extra_rc = 1;}isa = newisa;}
1、由initInstanceIsa调用的initIsa中nonpointer传的值为true,走的是else内部逻辑,对newisa多项赋值。
2、由obj->initIsa(cls);调用的initIsa中nonpointer传的值为false,走的是if内部逻辑,只对newisa的class设置即可。
3、在initIsa方法中,首先第一句就断言ASSERT(!isTaggedPointer());是否为TaggedPointer,如果是TaggedPointer则无法继续后续的逻辑,即无isa。
isa的内部结构以及nonpointer和TaggedPointer的解释
isa结构源码如下,是一个联合体+位域的结构:
联合体union内部成员为互斥存在,即联合体所占内存大小决定于内部最大成员所占大小。结构体struct则是“有容乃大”,占内存大小可参考002-OC对象原理探究 - 结构体内存对齐
union isa_t {isa_t() { } // 构造方法isa_t(uintptr_t value) : bits(value) { } // 构造方法uintptr_t bits;private:Class cls;public:#if defined(ISA_BITFIELD)struct {ISA_BITFIELD; // 此宏为isa内部位域的分配情况,源码在下面};bool isDeallocating() {return extra_rc == 0 && has_sidetable_rc == 0;}void setDeallocating() {extra_rc = 0;has_sidetable_rc = 0;}#endifvoid setClass(Class cls, objc_object *obj);Class getClass(bool authenticated);Class getDecodedClass(bool authenticated);};
其中ISA_BITFIELD此宏为isa内部位域的分配情况:(:1、:44等分别表示该成员占1位、44位)
# if __arm64__uintptr_t nonpointer : 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 unused : 1;uintptr_t has_sidetable_rc : 1;uintptr_t extra_rc : 19# elif __x86_64__uintptr_t nonpointer : 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 unused : 1;uintptr_t has_sidetable_rc : 1;uintptr_t extra_rc : 8
-nonpointer: 是否开启NONPOINTER isa指针优化;
-has_assoc: 对象是否含有关联引用
-has_cxx_dtor:对象是否含有 C++ 或者 Objc 的析构器
-shiftcls: 类的指针(重点)(arm64:33位,x86_64:44位)
-magic: 对象是否初始化完成 (arm64:0x16 ,x86_64:0x3b)
-weakly_referenced:是否为弱引用的对象
-deallocating:对象是否正在执行析构函数(是否在释放内存)
-has_sidetable_rc:判断是否需要用sidetable去处理引用计数,(extra_rc的大小影响到这个变量)
-extra_rc: 存储该对象的引用计数值减一后的结果
isa的二进制分布如图所示:
