这是我参与更文挑战的第5天,活动详情查看: [更文挑战]
前文回顾:
之前已经通过clang
查看过对象在C++
是什么样子的了,接下来结合源码分析下对象的本质。
isa探寻
首先在源码中查看objc_class的定义,并没有发现什么有用的价值。objc_class
只是包含了一个Class isa
,而Class
只是objc_class
的一个指针别名,好一个套娃。
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;
复制代码
接下来只能回顾到源码对象alloc
中,因为在过程中出现过initInstanceIsa
,只能顺着这个查看了。顺利找到关键步骤objc_object::initIsa
。isa_t
应该就是我们的isa
了。
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;
// 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
# if ISA_HAS_CXX_DTOR_BIT
newisa.has_cxx_dtor = hasCxxDtor;
# endif
newisa.setClass(cls, this);
#endif
newisa.extra_rc = 1;
}
// 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_t
是这样的。
union isa_t {
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
uintptr_t bits;
private:
// Accessing the class requires custom ptrauth operations, so
// force clients to go through setClass/getClass by making this
// private.
Class cls;
public:
#if defined(ISA_BITFIELD)
struct {
ISA_BITFIELD; // defined in isa.h
};
bool isDeallocating() {
return extra_rc == 0 && has_sidetable_rc == 0;
}
void setDeallocating() {
extra_rc = 0;
has_sidetable_rc = 0;
}
#endif
复制代码
然后在宏定义ISA_BITFIELD
终于找到了我们isa里面是存的啥了。
isa分析
nonpointer
nonpointer
表示对指针时候开启优化 0:纯isa指针,1:不止包含对象地址,还包含类信息、对象引用计数、弱引用等。
has_assoc
has_assoc
是否有设置过关联对象,如果没有,释放时会更快
has_cxx_dtor
has_cxx_dtor
是否有C++的析构函数(.cxx_destruct),如果没有,释放时会更快
shiftcls
shiftcls
存储着Class、Meta-Class对象的内存地址信息,在nonpointer=1的情况下,在arm64架构中有33位用来存储类指针 (重点)
magic
magic
用于在调试时分辨对象是否未完成初始化
weakly_referenced
weakly_referenced
是否有被弱引用指向过,如果没有,释放时会更快。弱引用表。
deallocating
deallocating
对象是否正在释放
has_sidetable_rc
has_sidetable_rc
引用计数器是否过大无法存储在isa中
如果为1,那么引用计数会存储在一个叫SideTable的类的属性中
extra_rc
extra_rc
里面存储的值是引用计数器减1,如果引用计数10这里面存的9,如果引用计数大于10则需要使用has_sidetable_rc
isa测试
既然已经发现了isa
的内存储,接下来就要测试下isa
了。