引言
在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 void
objc_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_ISA
ASSERT(cls->classArrayIndex() > 0);
newisa.bits = ISA_INDEX_MAGIC_VALUE;
newisa.has_cxx_dtor = hasCxxDtor;
newisa.indexcls = (uintptr_t)cls->classArrayIndex();
#else
newisa.bits = ISA_MAGIC_VALUE;
# if ISA_HAS_CXX_DTOR_BIT
newisa.has_cxx_dtor = hasCxxDtor;
# endif
newisa.setClass(cls, this);
#endif
newisa.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;
}
#endif
void 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的二进制分布如图所示:
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流程的一个坑。好累,拜拜。。
网友评论