一. isa 初始化
inline void
objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor)
{
assert(!isTaggedPointer());
//nonpointer 如果为 0 代表普通指针,如果为 1 代表优化后指针,位域存储更多信息
if (!nonpointer) {
//如果为普通指针,isa.cls 赋值 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
//x86_64下 ISA_MAGIC_VALUE 0x001d800000000001
//arm64下 0x000001a000000001
newisa.bits = ISA_MAGIC_VALUE;
//has_cxx_dtor为析构函数,这里标志为是否存在析构函数,如果没有释放更快
newisa.has_cxx_dtor = hasCxxDtor;
//shiftcls 类与元类的对象内存地址,由此可见 cls 与 isa 息息相关
newisa.shiftcls = (uintptr_t)cls >> 3;
#endif
isa = newisa;
}
}
二. isa 结构
union isa_t {
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
Class cls;
uintptr_t bits;
#if defined(ISA_BITFIELD)
//ISA_BITFIELD 宏定义,如下代码块
# if __arm64__
struct {
ISA_BITFIELD; // defined in isa.h
};
#endif
};
# if __arm64__
# define ISA_MASK 0x0000000ffffffff8ULL
# define ISA_MAGIC_MASK 0x000003f000000001ULL
# define ISA_MAGIC_VALUE 0x000001a000000001ULL
//ISA_BITFIELD共 64 位,标志位占一位nonpointer:表示是否对 isa 指针开启指针优化.0:纯isa指针,1:不止是类对象地址,isa 中包含了类信息、对象的引用计数等
标志位占一位has_assoc:关联对象标志位,0没有,1存在
标志位占一位has_cxx_dtor:该对象是否有 C++ 或者 Objc 的析构器,如果有析构函数,则需要做析构逻辑, 如果没有,则可以更快的释放对象
标志位占 33 位shiftcls:存储类指针的值。开启指针优化的情况下,在 arm64 架构中有 33 位用来存储类指针。
标志位占 6 位magic:用于调试器判断当前对象是真的对象还是没有初始化的空间
标志位占 1 位weakly_referenced:标志对象是否被指向或者曾经指向一个 ARC 的弱变量,没有弱引用的对象可以更快释放
标志位占 1 位deallocating:标志对象是否正在释放内存
标志位占 1 位has_sidetable_rc:当对象引用技术大于 10 时,则需要借用该变量存储进位
标志位占 19 位:当表示该对象的引用计数值,实际上是引用计数值减 1, 例如,如果对象的引用计数为 10,那么 extra_rc 为 9。如果引用计数大于 10, 则需要使用到下面的 has_sidetable_rc
# 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)
# else
# error unknown architecture for packed isa
# endif
通过objc_object我们可以追踪到 isa 其本质结构就是联合体.联合体共用内存.
三. isa 关于shiftcls的流程
首先创建一个类,并打上断点追踪.( 此环境是在 objc750 下修改的 MacOS 项目,感谢 Cooci 老师 )
//代码示例
LGPerson *object = [LGPerson alloc];
NSLog(@"Hello, World! %@",object); //断点处
//lldb 断点追踪
(lldb) x/4gx object //查看 object 对象内存结构
0x100f03590: 0x001d800100001139 0x0000000000000000
0x100f035a0: 0x0000000000000015 0x00007fff8c0c3df0
(lldb) p/x LGPerson.class //查看LGPerson.class 类内存结构
(Class) $1 = 0x0000000100001138 LGPerson
(lldb) p/t 0x0000000100001138 //LGPerson 16 进制转 2 进制
(long) $2 = 0b0000000000000000000000000000000100000000000000000001000100111000
(lldb) p/t 0x001d800100001139 //object对象 16 进制转 2 进制
(long) $3 = 0b0000000000011101100000000000000100000000000000000001000100111001
(lldb) p/t $3>>3<<3 //object 右移 3 左移 3 得到结果 $4
(long) $4 = 0b0000000000011101100000000000000100000000000000000001000100111000
(lldb) p/t $4<<17>>17 //$4 左移 17 右移 17 得到 $5, $5 = $2
(long) $5 = 0b0000000000000000000000000000000100000000000000000001000100111000
(lldb)
综上所述我们可以总结
- 对象第一个属性必然是isa
- 通过 isa 我们可以 object 找到 class
四. isa 走位
首先还是初始化类,lldb 调试
//代码示例
LGPerson *person = [LGPerson alloc];
(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
//由 person 得到 LGPerson 通过 isa
(lldb) x/4gx LGPerson.class
0x1000023b0: 0x001d800100002389 0x0000000100b37140
0x1000023c0: 0x00000001003da290 0x0000000000000000
(lldb) po 0x1000023b0
LGPerson
//得到LGPerson内存结构,打印目标首地址
(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
//(Class)(isa.bits & ISA_MASK);所以可通过 isa 一直追踪,得到NSObject
- 综上所述我们可以得到
- 在内存中对象可以创建多个
- 类只可以创建一个
- isa 走向:对象->元类 ->根元类->根元类
- NSObject的父类是nil,根元类的父类是NSObject
官方指向流程图
