isa的初始化和指向分析
2019-12-23 本文已影响0人
枫叶知秋
isa的初始化
isa的初始化流程代码
inline void
objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)
{
assert(!cls->instancesRequireRawIsa());
assert(hasCxxDtor == cls->hasCxxDtor());
initIsa(cls, true, hasCxxDtor);
}
// -> initIsa
inline void
objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor)
{
assert(!isTaggedPointer());
if (!nonpointer) {
// 如果单纯是个指针,就将类传过来的类赋值给isa的cls
isa.cls = 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;
newisa.has_cxx_dtor = hasCxxDtor;
newisa.indexcls = (uintptr_t)cls->classArrayIndex();
#else
newisa.bits = ISA_MAGIC_VALUE;
newisa.has_cxx_dtor = hasCxxDtor;// 标记是否存在c++ 析构函数
newisa.shiftcls = (uintptr_t)cls >> 3;
#endif
isa = newisa;
}
}
问题解释:为什么赋值了cls
之后就不赋值bits
了?
if (!nonpointer) {
`isa.cls = cls;`
}else{
...
`newisa.bits = ISA_INDEX_MAGIC_VALUE;`
...
}
原因是:isa
是一个命名为isa_t
的联合体,联合体中Class cls;
和uintptr_t bits;
是 互斥
的。
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初始化,是进行一系列参数赋值,如图所示:
通过代码
isa_t newisa(0);
和isa = newisa;
可以知道,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
};
其中在位域 struct { ISA_BITFIELD; // defined in isa.h };
中可以查到 isa_t的结构代码(分为arm64和x86两种)如下:
# 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)
isa_t结构代码的解释
- nonpointer 表示是否对isa指针开启优化(我们现在的都是开启了优化),值0:纯isa指针 值1:不止是类对象地址,isa中还包含了类信息,对象的引用计数等;
- has_assoc 是否有关联对象, 值0 没有 值1 有;
- has_cxx_dtor 该对象是否有c++或者objc析构函数,如果有析构函数,先走析构逻辑,没有就更快的释放对象;
- shiftcls存储类指针的值,开启指针优化的时候 在arm64架构中有33位存储类指正
- magic用于调试器判断当前对象是真的对象还是没有初始化的空间
- weakly_referenced 标志对象是否指向或者曾经指向一个ARC的弱变量,没有弱引用的对象可以更快的释放
- deallocating 标志对象是否正在释放
- has_sidetable_rc 当对象引用计数大于10时,则需要借用该变量存储进位
- extra_rc表示对象的引用计数的值,实际上是引用计数减1.
例如 如果对象的引用计数位10 则extra_rc为9 ,若引用计数大于10 ,则需要用到上面的has_sidetable_rc。
对象的第一个属性必然是isa
验证方法:
- NSObject 中定义第一属性就是isa
Class isa
- NSObject 中定义第一属性就是isa
@interface NSObject <NSObject> {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-interface-ivars"
Class isa OBJC_ISA_AVAILABILITY;
#pragma clang diagnostic pop
}
- 2.isa必然关联一个class isa <-> class
根据newisa.shiftcls = (uintptr_t)cls >> 3;
进行二进制位移后,后面相同,如图 截屏2019-12-22下午11.22.15.png
isa 指向分析
- 通过lldb进行调试可以发现
初始化类
LGPerson *person = [LGPerson alloc];
根据(Class)(isa.bits & ISA_MASK);
lldb调试如下
(lldb) x/4gx person
0x100f3b150: 0x001d8001000023b5 0x0000000000000000
0x100f3b160: 0x0000000000000000 0x0000000000000000
(lldb) p/x LGPerson.class
(Class) $1 = 0x00000001000023b0 LGPerson
(lldb) p/x 0x00000001000023b0 & 0x0000000ffffffff8
(long) $2 = 0x00000001000023b0
(lldb) po 0x00000001000023b0
LGPerson
通过调试可以得出:isa 从类的实例对象person指向了类LGPerson。
继续调试,打印LGPerson首地址也是LGPerson
(lldb) x/4gx LGPerson.class
0x1000023b0: 0x001d800100002389 0x0000000100b37140
0x1000023c0: 0x00000001003da290 0x0000000000000000
(lldb) po 0x1000023b0
LGPerson
根据(Class)(isa.bits & ISA_MASK);
找到最终类为NSObject
(lldb) x/4gx LGPerson.class
0x1000023b0: 0x001d800100002389 0x0000000100b37140
0x1000023c0: 0x00000001003da290 0x0000000000000000
(lldb) p/x 0x001d800100002389 & 0x0000000ffffffff8
(long) $8 = 0x0000000100002388
(lldb) po 0x0000000100002388
LGPerson
(lldb) x/4gx 0x0000000100002388
0x100002388: 0x001d800100b370f1 0x0000000100b370f0
0x100002398: 0x000000010104ee30 0x0000000400000007
(lldb) p/x 0x001d800100b370f1 & 0x0000000ffffffff8
(long) $10 = 0x0000000100b370f0
(lldb) po 0x0000000100b370f0
NSObject
(lldb) x/4gx 0x0000000100b370f0
0x100b370f0: 0x001d800100b370f1 0x0000000100b37140
0x100b37100: 0x000000010123d3e0 0x0000000400000007
(lldb) p/x 0x001d800100b370f1 & 0x0000000ffffffff8
(long) $12 = 0x0000000100b370f0
(lldb) po 0x0000000100b370f0
NSObject
通过上图调试可以完美验证isa的走位如图所示
isa 指向图
1129392-2fcd94dced3798a2.png
- 虚线代表了isa的走位。实例对象->类->元类->根元类->根根元类(根元类本身)。
- 实线代表着继承。
总结
- 实例对象的isa指向的是类;
- 类的isa指向的元类;
- 元类isa指向根元类;
- 根元类isa指向自己;
- NSObject的父类是nil,根元类的父类是NSObject。