在alloc&init&new探索中我们知道,alloc一个对象主要有cls->instanceSize
计算需要的内存大小、calloc(1, size)
根据所需要的内存大小开辟内存空间、initInstanceIsa(cls, hasCxxDtor)
将isa
与内关联;今天就来探索一下isa
是如何与类产生关联的。
准备工作:objc4-781源码
obj->initInstanceIsa(cls, hasCxxDtor);
if (!zone && fast) {
obj->initInstanceIsa(cls, hasCxxDtor);
} else {
// Use raw pointer isa on the assumption that they might be
// doing something weird with the zone or RR.
obj->initIsa(cls);
}
- 跟进
initInstanceIsa(cls, hasCxxDtor)
objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)
{
ASSERT(!cls->instancesRequireRawIsa());
ASSERT(hasCxxDtor == cls->hasCxxDtor());
initIsa(cls, true, hasCxxDtor);
}
- 这我们看出,接下来会执行
initIsa(cls, true, hasCxxDtor)
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;
}
}
跟到这里我们可以看到一直困扰我们的isa
是在这里完成配置的。 isa
的类型是isa_t
。那么isa
到到底是怎么配置的呢?
isa_t
我们跟进isa_t
发现isa_t
是一个联合体
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_t
类型使用联合体
是基于内存优化
,采用char+位域
(即二进制中每一位均可以保存不同的信息)的原理。我们都知道isa指针
占用8
个字节,即64
位,可以合理的将64
位利用起来,极大的节约了内存空间。
- 联合体
isa_t
提供了两个成员变量cls
、bits
,由联合体定义可知cls
与bits
互斥。 - 还提供了一个
位域机构体
,在这个位域机构体中是一个宏ISA_BITFIELD
,根据释义我们知道这个宏定义在isa.h
中- 位域结构体
按二进制位来分配内存的结构体,冒号表示位域。位域出现的原因是由于某些信息的存储表示只需要几个bit位就可以表示而不需要一个完整的字节,同时也是为了节省存储空间和方便处理
- 位域结构体
ISA_BITFIELD
# 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)
# else
# error unknown architecture for packed isa
# endif
我们可以看出整个定义的联合体位域无论是在__arm64__ (iOS)
还是__x86_64__(macOS)
中都占64
位。32
位这里就探索了,一样的原理。
- nonpointer:是否对
isa指针
开启指针优化
。有两个值,表示自定义的类等,占1位- 0:纯isa指针
- 1:不只是
类对象地址
,isa中包含了类信息、对象的引用计算等
- has_assoc:是否有
关联对象
- has_cxx_dtor:是否有
C++
的相关实现。表示该对象是否有C++/OC
的析构器
(类似于dealloc),占1位- 如果有
析构函数
,则需要做析构相关的逻辑 - 如果没有,则可以快速
释放
- 如果有
- shiftcls:存储类信息
-
__arm64__
中占33位 -
__x86_64__
中占44位
-
- magic:用于调试器判断
当前对象
是真的对象
还是没有初始化的空间
,占6位-
__arm64__
: 0x1a -
__x86_64__
:0x3b
-
- weakly_referenced:指对象是否
被指向
或者曾经指向一个ARC的弱变量
- 如果没有可以更快释放
- deallocating:标志对象是否正在释放内存
- has_sidetable_rc:是否有外挂的
散列表
。当对象的引用计数大于10时,则需要借用该变量存储进位 - extra_rc:额外的引用计数,实际上是引用计数值减1,也就是说如果对象引用计数为10,那么extra_rc是9.如果引用计数大于10,则需要用到
has_sidetable_rc
isa
从这张图中我们可以看到类的实例的
isa
指向了类
,类
的isa
指向的它的元类
,而元类的isa
指向了根元类
,根元类的isa
还是指向本身
,但是根元类NSObject
也继承自类NSObject
。