了解对象
Objective-C是一门面向对象编程语言。对象是什么,我们这篇文章讲的isa和对象又有什么样的关系呢?
带着疑问我们可以去看看苹果开源源码。
在其中找到了对object的定义:
/// Represents an instance of a class.
struct objc_object {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
};
objc_object表示类的实例,就是我们通常说的对象。
LGPerson *objc = [[LGPerson alloc] init];
Class p1 = [objc class];
Class p2 = [LGPerson class];
打印结果p1:0x1000020f8 p2:0x1000020f8
struct objc_class : objc_object {
// Class ISA;
Class superclass;
cache_t cache; // formerly cache pointer and vtable
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
...
}
p1通过object_getClass获取到isa指向的类对象,p2通过类名也获取到一摸一样的对象。
从源码可以看到objc_class是继承自objc_object且其中也有isa。这是不是意味着类也是读对象。而且我们还可以得出一个结论类对象在内存中仅存在一份。
isa是什么
我们在研究对象初始化的时候,曾经跟随底层源码看到了其中有这么一段代码
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);
...
isa = newisa;
}
}
isa_t isa;
无论nonpointer条件YES还是NO,最终返回的都是isa_t类型的isa。我们继续去看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
};
__arm64__
# 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
ISA_BITFIELD中就是isa的数据结构定义。
- nonpointer:表示是否对 isa 指针开启指针优化(0:纯 isa 指针;1:不止是类对象地址,isa中包含了类信息、对象的引用计数等)
- has_assoc:关联对象标志位;0没有,1存在
- has_cxx_dtor:该对象是否有 C++ 或者 Objc 的析构器(dealloc等),如果有析构函数,则需要做析构逻辑,如果没有,则可以更快的释放对象
- shiftcls:存储类指针的值。开启指针优化的情况下,在 arm64 架构中有 33 位用来存储类指针。
- magic:用于调试器判断当前对象是真的还是没有初始化的空间
- weakly_referenced:指对象是否被指向或者曾经指向一个 ARC 的弱变量,没有弱引用的对象可以更快的释放。
- deallocating:标志对象是否正在释放内存。
- has_sidetable_rc:当对象引用计数大于 10 时,则需要借用该变量存储进位。(是否有外挂的散列表)
- extra_rc:额外的引用计数;表示该对象的引用计数值,实际上是引用计数值减1。(eg:如果对象的引用计数为10,那么 extra_rc 为9。如果应用计数大于 10,则需要使用到 has_sidetable_rc)。
isa与类关联
对象可以通过isa获取到类信息,cls 与 isa 关联原理就是isa指针中的shiftcls位域中存储了类信息,我们可以通过以下方式验证:
1.我们通过断点,通过lldb调试看newisa.shiftcls赋值前后newisa的变化
赋值前
(lldb) p newisa
(isa_t) $0 = {
cls = 0x001d800000000001
bits = 8303511812964353
= {
nonpointer = 1
has_assoc = 0
has_cxx_dtor = 0
shiftcls = 0
magic = 59
weakly_referenced = 0
deallocating = 0
has_sidetable_rc = 0
extra_rc = 0
}
}
赋值后
(lldb) p newisa
(isa_t) $1 = {
cls = LGPerson
bits = 8303516107940081
= {
nonpointer = 1
has_assoc = 0
has_cxx_dtor = 0
shiftcls = 536871966
magic = 59
weakly_referenced = 0
deallocating = 0
has_sidetable_rc = 0
extra_rc = 0
}
}
在newisa.shiftcls = (uintptr_t)cls >> 3这一步之后,cls变成了LGPerson,bits中也只有shiftcls发生了变化,说明类的信息存入了shiftcls中。
2.通过 isa & ISA_MSAK
注意:
- arm64中,ISA_MASK 宏定义值0x0000000ffffffff8ULL
- x86_64中,ISA_MASK 宏定义值0x00007ffff
在_class_createInstanceFromZone方法中,initIsa之后
(lldb) po obj
<LGPerson: 0x100713e90>
(lldb) x/4gx obj
0x100713e90: 0x001d8001000020f1 0x0000000000000000
0x100713ea0: 0x72616553534e5b2d 0x20646c6569466863
(lldb) po 0x001d8001000020f1 & 0x0000000ffffffff8ULL
LGPerson
(lldb)