几乎所有的类都是继承自NSObject开始实现的,所有这篇文章我们也将从NSObject类开始来了解对象的创建及isa,文章也将从runtime源码开始让读者了解isa究竟是什么东西。
一:对象分解
NSObject* object = [[NSObject alloc] init];
NSObject* object = (((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)
((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"));
分析:上面我通过命令 clang -rewrite-objc xx.m 将OC代码转换为C++源码,我们会发现这里其实就是先调用alloc方法,再进一步调用init方法。
二:对象创建过程
// NSObject.mm
+ (id)alloc {
return _objc_rootAlloc(self);
}
id _objc_rootAlloc(Class cls)
{
return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}
callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
if (fastpath(!cls->ISA()->hasCustomAWZ())) {
if (fastpath(cls->canAllocFast())) { 存在快速alloc方法实现
bool dtor = cls->hasCxxDtor(); 获取对象是否存在析构器
id obj = (id)calloc(1, cls->bits.fastInstanceSize()); 创建对象
if (slowpath(!obj)) return callBadAllocHandler(cls); 若失败则执行修复对象内部操作
obj->initInstanceIsa(cls, dtor); 初始化对象
return obj;
}
else {
id obj = class_createInstance(cls, 0); 创建对象
return obj;
}
}
}
分析:对象创建过程程序会调用_objc_rootAlloc方法,再通过方法callAlloc去实现对象地创建。
三:对象初始化
objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)
{
initIsa(cls, true, hasCxxDtor); 初始化方法
}
objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor)
{
if (!nonpointer) {
isa.cls = cls;
} else {
isa_t newisa(0);
#if SUPPORT_INDEXED_ISA
newisa.bits = ISA_INDEX_MAGIC_VALUE;
newisa.has_cxx_dtor = hasCxxDtor;
newisa.indexcls = (uintptr_t)cls->classArrayIndex();
#else iOS执行这部分代码
newisa.bits = ISA_MAGIC_VALUE;
newisa.has_cxx_dtor = hasCxxDtor;
newisa.shiftcls = (uintptr_t)cls >> 3;
#endif
isa = newisa;
}
}
执行initInstanceIsa方法进行对象参数的初始化。
# define ISA_MAGIC_VALUE 0x001d800000000001ULL
ISA_MAGIC_VALUE的值转换成二进制,我们会发现isa_t的magic和nonpointer都被进行了初始化设置。
nonpointer为0时,访问对象isa会返回一个指向cls的指针。也就是在 iPhone 迁移到 64 位系统之前时 isa 的类型;
nonpointer为1时,shiftcls才是指向cls的指针,也即是isa对象地址将是存放在shiftcls中,但isa还是保存了一些cls的信息。
magic则是判断对象是否已经初始化;
has_cxx_dtor是判断对象是否存在析构器,若不存在的话,内存释放将会加快;
newisa.shiftcls = (uintptr_t)cls >> 3;
shiftcls是当前对象指向了类cls的指针。假设当前类cls地址为100000000001110101110000011111000,由于后面三位都为0,所以右移3位可以将不需要用到的位空出来,为 isa
留下 34 位用于性能的优化。
四:isa_t对象的其它属性:
union isa_t
{
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
Class cls;
uintptr_t bits;
# if __arm64__
# define ISA_MASK 0x0000000ffffffff8ULL
# define ISA_MAGIC_MASK 0x000003f000000001ULL
# define ISA_MAGIC_VALUE 0x000001a000000001ULL
struct {
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;
};
}
has_assoc是判断对象是否有关联,没有可加速内存地释放;
weakly_referenced是对象指向弱引用的变量,没有也可加速内存释放;
deallocating是对象正在释放中;
has_sidetable_rc是对象的引用计数太大了;
extra_rc是对象引用计数减1;
总结:
以上则是对NSObject对象的objc源码解析及isa类指针信息的说明,只不过现在直接获取isa对象是不允许,直接会报错误,毕竟有些对象类似NSString有时候被优化成TaggedPointer指针了,其实不是一个对象,不能通过isa对其进行操作。
参考文献: