isa是什么?
这里是苹果对isa给出的官方解释
A pointer to the class definition of which this object is an instance.
翻译过来就是“一个指向当前实例对象所属的类的指针”
简单来说就是通过isa知道当前对象到底是什么东西,比如一个人作为一个对象,他有很多属性,如身高年龄性别等,但通过这些属性并不知道这个对象到底是个什么东西,那么isa就是用来指向说这个对象是人类。也就是对象通过isa可以找到其根源。
联合体(union)的概念
- 利用
union
可以用相同的存储空间存储不同型别的数据类型,从而节省内存空间。当访问其内成员时可用"."和"->"来直接访问。 - 当多个数据需要共享内存或者多个数据每次只取其一时,可以利用联合体(
union
)。在C Programming Language
一书中对于联合体是这么描述的: - 联合体是一个结构;
- 它的所有成员相对于基地址的偏移量都为0;
- 此结构空间要大到足够容纳最"宽"的成员;
- 其对齐方式要适合其中所有的成员;
isa创建流程
inline void
objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)
{
ASSERT(!cls->instancesRequireRawIsa());
ASSERT(hasCxxDtor == cls->hasCxxDtor());
initIsa(cls, true, hasCxxDtor);
}
inline void
objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor)
{
ASSERT(!isTaggedPointer());
if (!nonpointer) {
isa = isa_t((uintptr_t)cls);
} else {
ASSERT(!DisableNonpointerIsa);
ASSERT(!cls->instancesRequireRawIsa());
isa_t newisa(0);
#if SUPPORT_INDEXED_ISA
ASSERT(cls->classArrayIndex() > 0);
newisa.bits = ISA_INDEX_MAGIC_VALUE;
// isa.magic is part of ISA_MAGIC_VALUE
// isa.nonpointer is part of ISA_MAGIC_VALUE
newisa.has_cxx_dtor = hasCxxDtor;
newisa.indexcls = (uintptr_t)cls->classArrayIndex();
#else
newisa.bits = ISA_MAGIC_VALUE;
// isa.magic is part of ISA_MAGIC_VALUE
// isa.nonpointer is part of ISA_MAGIC_VALUE
newisa.has_cxx_dtor = hasCxxDtor;
newisa.shiftcls = (uintptr_t)cls >> 3;
#endif
// This write must be performed in a single store in some cases
// (for example when realizing a class because other threads
// may simultaneously try to use the class).
// fixme use atomics here to guarantee single-store and to
// guarantee memory order w.r.t. the class index table
// ...but not too atomic because we don't want to hurt instantiation
isa = newisa;
}
}
我们通过查看alloc
源码找到了initInstanceIsa
的方法,点进来后里面就是创建isa
以及关联类的流程,我们在此源码基础上找到isa的内部结构isa = isa_t((uintptr_t)cls);
isa的内部结构
我们通过查看isa_t发现isa里面是一个联合体
union isa_t {
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
Class cls;
uintptr_t bits;
#if defined(ISA_BITFIELD)
struct {
ISA_BITFIELD; // defined in isa.h
};
#endif
};
- 通过查看
ISA_BITFIELD
里面的结构得到下面的代码,分别是在arm64
和x86_64
架构下的一个成员分布情况
# if __arm64__
# define ISA_MASK 0x0000000ffffffff8ULL
# define ISA_MAGIC_MASK 0x000003f000000001ULL
# define ISA_MAGIC_VALUE 0x000001a000000001ULL
# define ISA_BITFIELD \
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 deallocating : 1; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 19
# define RC_ONE (1ULL<<45)
# define RC_HALF (1ULL<<18)
# elif __x86_64__
# define ISA_MASK 0x00007ffffffffff8ULL
# define ISA_MAGIC_MASK 0x001f800000000001ULL
# define ISA_MAGIC_VALUE 0x001d800000000001ULL
# define ISA_BITFIELD \
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 deallocating : 1; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 8
# define RC_ONE (1ULL<<56)
# define RC_HALF (1ULL<<7)
NONPOINTER_ISA 分析
这里我们仅仅对arm64架构下的结构进行分析
isa内部成员 | 意义 |
---|---|
nonpointer |
表示是否对 isa 指针开启指针优化 0:纯isa指针,1:不止是类对象地址,isa 中包含了类信息、对象的引用计数等 |
has_assoc |
关联对象标志位,0没有,1存在 |
has_cxx_dtor |
该对象是否有 C++ 或者 Objc 的析构器,如果有析构函数,则需要做析构逻辑, 如果没有,则可以更快的释放对象 |
shiftcls |
存储类指针的值。开启指针优化的情况下,在 arm64 架构中有 33 位用来存储类指针 |
magic |
用于调试器判断当前对象是真的对象还是没有初始化的空间 |
weakly_referenced |
对象是否被指向或者曾经指向一个 ARC 的弱变量,没有弱引用的对象可以更快释放。 |
deallocating |
标志对象是否正在释放内存 |
has_sidetable_rc |
当对象引用技术大于 10 时,则需要借用该变量存储进位 |
extra_rc |
当表示该对象的引用计数值,实际上是引用计数值减 1, 例如,如果对象的引用计数为 10,那么 extra_rc 为 9。如果引用计数大于 10, 则需要使用到下面的has_sidetable_rc
|
通过dealloc查看对象释放情况
通过查看dealloc
源码发现对象在释放的时候也要对isa
里面信息进行清除,这里的成员在我们前面看到的isa
内部成员完全符合。
inline void
objc_object::rootDealloc()
{
if (isTaggedPointer()) return;
if (fastpath(isa.nonpointer &&
!isa.weakly_referenced &&
!isa.has_assoc &&
!isa.has_cxx_dtor &&
!isa.has_sidetable_rc))
{
assert(!sidetable_present());
free(this);
}
else {
object_dispose((id)this);
}
}
isa与类进行关联
通过前面我们知道了isa
其实包含了很多信息,但是isa
是怎么和我们的类进行绑定的呢?我们再次查看创建流程的源码,进行断点调试发现在初始化isa
后有这么一句代码
newisa.shiftcls = (uintptr_t)cls >> 3;
这句话是什么意思呢?我们知道shiftcls
包含了类指针信息,这里将cls
编码后右移了三位,cls
正是我们的创建的类名字,在这句话之前我们打断点打印刚创建的newisa
发现:
我们可以看到打印的newisa
里面正好就有了前面所提到的那些结构,然后通过手动操作对cls
编码进行右移三位
我们发现打印的结果中shiftcls
里面的值变成了cls
的数值536871965
,cls也变成了LGPerson
,这里说明我们的类已经和新创建的isa绑定起来了,最后再返回该isa
就完成了。