引入对象的本质
通过查看底层代码,了解到对象创建的流程。最主要的代码主要是
size = cls->instanceSize(extraBytes);
obj = (id)calloc(1, size);
obj->initIsa(cls);
复制代码
主要步骤是:
- 计算Class内存大小;
- 根据size开辟内存空间;
- 将obj和cls绑定在一起。
前两个步骤不难理解,我们接下来研究initIsa做了什么。进入initIsa()
。
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;
}
复制代码
从上面的代码不难看出setClass(cls, this)
是主要的步骤,其他的就是些判断和初始化赋值。这里同时引入了新的东西,那就是isa_t
。
isa_t
进入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
void setClass(Class cls, objc_object *obj);
Class getClass(bool authenticated);
Class getDecodedClass(bool authenticated);
};
复制代码
首先可以看到isa_t是union
,union即是联合体,联合体类似结构体,其内的成员公用同一片空间,它的内存大小就是其内最大成员的大小,最大的区别就是联合体内的成员是互斥
的,其优点是使内存的使用更加的精细灵活,节省内存空间。
另一个重要的成员就是ISA_BITFIELD
,如下:
# if __arm64__
// ARM64 simulators have a larger address space, so use the ARM64e
// scheme even when simulators build for ARM64-not-e.
# if __has_feature(ptrauth_calls) || TARGET_OS_SIMULATOR
# define ISA_BITFIELD \
uintptr_t nonpointer : 1; \
uintptr_t has_assoc : 1; \
uintptr_t weakly_referenced : 1; \
uintptr_t shiftcls_and_sig : 52; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 8
# else
# 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 unused : 1; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 19
# endif
# elif __x86_64__
# define ISA_BITFIELD \
uintptr_t nonpointer : 1; // 表示是否对 isa 指针开启指针优化 0:纯isa指针,1:不止是类对象地址,isa 中包含了类信息、对象的引用计数等 \
uintptr_t has_assoc : 1; // 关联对象标志位,0没有,1存在 \
uintptr_t has_cxx_dtor : 1; // 该对象是否有 C++ 或者 Objc 的析构器,如果有析构函数,则需要做析构逻辑, 如果没有,则可以更快的释放对象 \
uintptr_t shiftcls : 44; // 存储类指针的值。开启指针优化的情况下,在 arm64 架构中有 33 位用来存储类指针 /*MACH_VM_MAX_ADDRESS 0x7fffffe00000*/ \
uintptr_t magic : 6; // 用于调试器判断当前对象是真的对象还是没有初始化的空间 \
uintptr_t weakly_referenced : 1; // 志对象是否被指向或者曾经指向一个 ARC 的弱变量,
没有弱引用的对象可以更快释放。 \
uintptr_t unused : 1; // 标志对象是否在被释放内存 \
uintptr_t has_sidetable_rc : 1; // 当对象引用技术大于 10 时,则需要借用该变量存储进位 \
uintptr_t extra_rc : 8 // 当表示该对象的引用计数值,实际上是引用计数值减 1, 例如,如果对象的引用计数为 10,那么 extra_rc 为 9。如果引用计数大于 10, 则需要使用到下面的 has_sidetable_rc
# else
# error unknown architecture for packed isa
# endif
复制代码
从ISA_BITFIELD
中可以看出使用位域存放了很多信息,并且按照系统进行了区分。
位域
是指:有些数据在存储时并不需要占用一个完整的字节,只需要占用一个或几个二进制位即可。例如开关只有通电和断电两种状态,用 0 和 1 表示足以,也就是用一个二进位。正是基于这种考虑,C语言又提供了一种叫做位域的数据结构。位域可以实现节省空间的目的。
位域的尺寸大小是根据每个成员实际占用的位数来计算(不用按照结构体三大法则进行补位),最终结果必须是其内最大成员大小的整数倍。
那么要怎么在ISA_BITFIELD
中取出class信息呢,只需要用isa的数据与上ISA_MASK
即可。
在Class getClass(bool authenticated);
里也能看到class的获取方式。
inline Class
isa_t::getClass(MAYBE_UNUSED_AUTHENTICATED_PARAM bool authenticated) {
#if SUPPORT_INDEXED_ISA
return cls;
#else
uintptr_t clsbits = bits;
# if __has_feature(ptrauth_calls)
# if ISA_SIGNING_AUTH_MODE == ISA_SIGNING_AUTH
// Most callers aren't security critical, so skip the
// authentication unless they ask for it. Message sending and
// cache filling are protected by the auth code in msgSend.
if (authenticated) {
// Mask off all bits besides the class pointer and signature.
clsbits &= ISA_MASK;
if (clsbits == 0)
return Nil;
clsbits = (uintptr_t)ptrauth_auth_data((void *)clsbits, ISA_SIGNING_KEY, ptrauth_blend_discriminator(this, ISA_SIGNING_DISCRIMINATOR));
} else {
// If not authenticating, strip using the precomputed class mask.
clsbits &= objc_debug_isa_class_mask;
}
# else
// If not authenticating, strip using the precomputed class mask.
clsbits &= objc_debug_isa_class_mask;
# endif
# else
clsbits &= ISA_MASK;
# endif
return (Class)clsbits;
#endif
}
复制代码
总结
- 对象的本质就是
isa
,里面存储了对象的基本信息。 - isa使用是
联合体
和位域
,大大的节省的内存空间。
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END