[Cls alloc] 在计算完大小,并且分配空间之后,就去执行initInstanceIsa。现在继续跟着源码看下initInstanceIsa 是起到什么作用,做了那些操作
isa_t 的定义
是一个共用体,成员包括cls,bits,和一个结构体数据。公用一个内存,同时只会代表一个数据。
使用不同的位域表示不同的属性比如bool类型的属性,可以节省大量的存储空间
union 共用体/联合体
- 共用体的所有成员占用同一段内存,修改一个成员,也会影响其他成员的数据。可以看作同时只有一个成员是有准确数据的。
union isa_t {
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
Class cls;
uintptr_t bits;
#if defined(ISA_BITFIELD)
struct { // 是一个结构体位域成员,结构题的具体成员在ISA_BITFIELD中定义
ISA_BITFIELD; // defined in isa.h
};
#endif
};
// ISA_BITFIELD 真机arm64 下的定义
# define ISA_BITFIELD
// : 1; 数字代表这个占据了多少个0/1位。
uintptr_t nonpointer : 1;// 代表普通的指针,存储着Class、Meta-Class对象的内存地址。1,代表优化过,使用位域存储更多的信息
uintptr_t has_assoc : 1; //是否存在关联对象,如果没有,释放时会更快
uintptr_t has_cxx_dtor : 1; //是否有C++的析构函数(.cxx_destruct),如果没有,释放时会更快。有的话需要做析构操作。
uintptr_t shiftcls : 33; // 存储着类信息。cls >> 3 对象是否完成初始化 存储着Class、Meta-Class对象的内存地址信息
/*MACH_VM_MAX_ADDRESS 0x1000000000*/
uintptr_t magic : 6; // 用于在调试时分辨对象是真的对象还是没有初始化的空间
uintptr_t weakly_referenced : 1; // 是否有被弱引用指向过,如果没有,释放时会更快
uintptr_t deallocating : 1; // 对象是否正在释放
uintptr_t has_sidetable_rc : 1; // 当对象引⽤技术⼤于 10 时,则需要借⽤该变量存储进位
如果为1,那么引用计数会存储在一个叫SideTable的类的属性中
uintptr_t extra_rc : 19 // 当表示该对象的引⽤计数值,实际上是引⽤计数值减 1,
例如,如果对象的引⽤计数为 10,那么 extra_rc 为 9。如果引⽤计数⼤于 10,
则需要使⽤到下⾯的 has_sidetable_rc。
![](https://img.haomeiwen.com/i11380959/a5615a73009e91a6.jpeg)
结构体位域
有些信息在存储时,并不需要占用一个完整的字节, 而只需占几个或一个二进制位。比如一个Bool类型,只有0和1 两种状态, 用一位二进位即可。为了节省存储空间,并使处理简便,C语言又提供了一种数据结构,称为“位域”或“位段”。
所谓“位域”是把一个字节中的二进位划分为几 个不同的区域,并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。 这样就可以把几个不同的对象用一个字节的二进制位域来表示。
alloc中调用:initInstanceIsa -> initIsa
inline void objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)
{
ASSERT(!cls->instancesRequireRawIsa());
ASSERT(hasCxxDtor == cls->hasCxxDtor());
//
initIsa(cls, true, hasCxxDtor);
}
inline void objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor)
{
ASSERT(!isTaggedPointer());
// 根据上面initIsa(cls, true, hasCxxDtor)调用; 传入的是true。进入else
if (!nonpointer) {
isa = isa_t((uintptr_t)cls);
} else {
ASSERT(!DisableNonpointerIsa);
ASSERT(!cls->instancesRequireRawIsa());
// 一个新的 isa_t的结构体。
isa_t newisa(0);
// #if __ARM_ARCH_7K__ >= 2 || (__arm64__ && !__LP64__)
// # define SUPPORT_INDEXED_ISA 1
// #else
// # define SUPPORT_INDEXED_ISA 0
// #endif
// SUPPORT_INDEXED_ISA 在arm64 下可以看作false
#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; // 包含了 magic,nonpointer 信息
// isa.magic is part of ISA_MAGIC_VALUE
// isa.nonpointer is part of ISA_MAGIC_VALUE
newisa.has_cxx_dtor = hasCxxDtor;
newisa.shiftcls = (uintptr_t)cls >> 3;//shiftcls 类指针的非零位 Class pointer's non-zero bits.
#endif
// 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;
}
}
- 创建新的isa_t 联合体
- 使用 ISA_MAGIC_VALUE初始化赋值magic 以及nonpointer 为1
- 复制has_cxx_dtor
- 将cls的地址右偏移3位(8的倍数,类的地址也是8的倍数,抹去后面的3个0,省空间。另外使用掩码获取cls时也不需要再移位)赋值给shiftcls。
ISA_MAGIC_VALUE 对应的数据
可以结合isa_t 的定义 看下各属性对应的值
# if __arm64__
# define ISA_MASK 0x0000000ffffffff8ULL // shiftcls 的掩码。isa & ISA_MASK 可以获得 shiftcls
# define ISA_MAGIC_MASK 0x000003f000000001ULL
# define ISA_MAGIC_VALUE 0x000001a000000001ULL
![](https://img.haomeiwen.com/i11380959/52af10a1bb6f02fb.png)
位运算验证isa数据
调试过程中newisa 赋值前后的数据变化
// arm64 ISA_MAGIC_VALUE 0x000001a000000001ULL
//
// p newisa
(isa_t) $8 = {
cls = nil
bits = 0
= {
nonpointer = 0
has_assoc = 0
has_cxx_dtor = 0
shiftcls = 0
magic = 0
weakly_referenced = 0
deallocating = 0
has_sidetable_rc = 0
extra_rc = 0
}
}
// newisa 赋值之后
// p newisa
(isa_t) $17 = {
cls = LGPerson
bits = 8303516107940541
= {
nonpointer = 1 //代表优化过
has_assoc = 0
has_cxx_dtor = 1 //有C++的析构函数
shiftcls = 536872023 // 类地址向右移动3位
magic = 59 //
weakly_referenced = 0
deallocating = 0
has_sidetable_rc = 0
extra_rc = 0
}
}
// newisa 对应的二进制数据 0b0000000000011101100000000000000100000000000000000010001010111101
// 可以看出执行的效果是将 nonpointer,has_cxx_dtor,shiftcls,bits进行了赋值
inline Class objc_object::ISA()
{
ASSERT(!isTaggedPointer());
#if SUPPORT_INDEXED_ISA
if (isa.nonpointer) {
uintptr_t slot = isa.indexcls;
return classForIndex((unsigned)slot);
}
return (Class)isa.bits;
#else
return (Class)(isa.bits & ISA_MASK); // isa & ISA_MASK 即可获取shiftcls << 3 。即 cls的地址
#endif
}
验证 isa的数据
在上面学习到对象的isa数据中存储着起类的地址。那么去查看类的存储空间中,存储哪些数据
@interface LGPerson : NSObject {
int a_a;
char b_b;
NSString * c_c;
}
@property (strong, nonatomic) NSString * aaaaaa;
@property (assign, nonatomic) int bbbbb;
@property (assign, nonatomic) int ccccc;
@property (strong, atomic) NSString * ddddd;
@property (copy, nonatomic)NSString * eeeee;
@end
NSObject * p = [LGPerson alloc];
(lldb) x/2xg p
0x1006499e0: 0x001d8001000022bd 0x0000000000000000
(lldb) p/x 0x001d8001000022bd & 0x0000000ffffffff8 // 0x0000000ffffffff8 是ISA_MASK isa的掩码直接获取只想cls地址
(long) $2 = 0x00000001000022b8
(lldb) p/x LGPerson.class // 看下LGPerson类信息的存储地址)I
(Class) $1 = 0x00000001000022b8 LGPerson // 和实例对象isa中地址相同
(lldb) x/2xg 0x00000001000022b8 // 现在LGPerson 的内部存储的数据 isa的指针 0x0000000100002290
0x1000022b8: 0x0000000100002290 0x0000000100333140 // 0x0000000100002290 是isa的数据;0x0000000100333140 是superclass NSObject
(lldb) p/x 0x0000000100002290 & 0x0000000ffffffff8 // 获取isa中的cls指针
(long) $2 = 0x0000000100002290 // 说明类的isa指针就是一个单纯的cls指针
(lldb) p/x objc_getMetaClass("LGPerson") // 看下LGPerson的mata的ckass
(Class) $4 = 0x0000000100002290 // 类中的isa指针指向的是LGPerson的mataClass
(lldb) x/4xg 0x0000000100002290 // 看下mataClass的isa指针
0x100002290: 0x00000001003330f0 0x00000001003330f0 //
0x1000022a0: 0x00000001018070c0 0x0001e03500000007
(lldb) po 0x00000001003330f0 //可以得知mataClass的isa指针应该和NSObject有关系
NSObject
(lldb) x/4xg 0x00000001003330f0 // 然后后续只想的都是相同的地址 0x00000001003330f0
0x1003330f0: 0x00000001003330f0 0x0000000100333140
0x100333100: 0x000000010194bc40 0x0005e03100000007
(lldb) p/x NSObject.class
(Class) $15 = 0x0000000100333140 // NSObject // NSObject 的地址是 0x0000000100333140
(lldb) x/4xg 0x0000000100333140 // 查看NSObject 的isa指向 0x00000001003330f0
0x100333140: 0x00000001003330f0 0x0000000000000000
0x100333150: 0x000000010064a370 0x0001801000000003
(lldb) p/x objc_getMetaClass("NSObject") // 可以知道 0x00000001003330f0 是NSObject的元类地址
(Class) $9 = 0x00000001003330f0
![](https://img.haomeiwen.com/i11380959/3d5d75260c16ae95.png)
i386是针对intel通用微处理器32位处理器
x86_64是针对x86架构的64位处理器
模拟器32位处理器测试需要i386架构,
模拟器64位处理器测试需要x86_64架构,
真机32位处理器需要armv7,或者armv7s架构,
真机64位处理器需要arm64架构
网友评论