isa详解(一)isa结构
- 2019 年 10 月 8 日
- 筆記
从iOS 源码中我们可以找到以下结构体 。从这里我们开始看一下isa的构成
struct objc_object { private: isa_t isa; }; 复制代码
union isa_t { Class cls; uintptr_t bits; #if defined(ISA_BITFIELD) // 我们看一下这个宏 struct { ISA_BITFIELD; // defined in isa.h }; #endif }; 我们截取了arm64部分的宏定义 复制代码
# 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) 复制代码
所以在arm64环境下isa如下所示
union isa_t { Class cls; uintptr_t bits; 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 } }; 复制代码
所以为了探究isa 我们先研究下union
1. union
为什么要用union以及位运算呢。因为在计算机中为二进制。位运算是最快速的计算方式 union C++ 中的共用体。顾名思义 就是在union 中 公用一个内存地址 。
结构体 位域 : 1 只占一位
struct { char a : 1; //1位 char b : 1; //1位 char c : 1; //1位 } temp; //3位 1个字节 struct { char a; //1个字节 char b; //1个字节 char c; //1个字节 } temp; // 3个字节 union { char bits; // 一个字节 // struct 只是可读性,有和没有都一样 struct { char a : 1; //1位 char b : 1; //1位 char c : 1; //1位 } temp; } unionInstance 复制代码
union的内存结构如下图所示,两个指针指针同时管理一个内存地址。因为我们不操作struct中的 所以struct只是增强可读性。表示每个字节代表的含义。

2. 如果我们想访问union 中a,b,c的值如何设置 如何访问呢
#import "Person.h" #define aMask (1<<0) #define bMask (1<<1) #define cMask (1<<2) @interface Person() { union { char bits; struct { int a : 1; int b : 1; int c : 1; } temp; } _property; } @end @implementation Person - (instancetype)init { self = [super init]; if (self) { self->_property.bits = 0b00000000; } return self; } - (void)setAvar:(BOOL) a { if (a == YES) { self->_property.bits |= aMask; }else { self->_property.bits = (self->_property.bits & ~aMask); } } - (BOOL)a { return !!(self->_property.bits & aMask); } - (void)setBvar:(BOOL) b { if (b == YES) { self->_property.bits |= bMask; }else { self->_property.bits = (self->_property.bits & ~bMask); } } - (BOOL)b { return !!(self->_property.bits & bMask); } - (void)setCvar:(BOOL) c { if (c == YES) { self->_property.bits |= cMask; }else { self->_property.bits = (self->_property.bits & ~cMask); } } - (BOOL)c { return !!(self->_property.bits & cMask); } @end 复制代码
Person *person = [Person new]; [person setAvar:NO]; [person setBvar:YES]; [person setCvar:NO]; NSLog(@"a = %d, b = %d, c = %d",person.a,person.b,person.c); 复制代码
输出结果为
runtime-isa详解01[8059:19727104] a = 0, b = 1, c = 0 复制代码
isa指向
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); #endif } 复制代码
我们知道isa是指向class 或者是meta-class
但是源码上为什么要 & ISA_MASK 呢。
从上面的有关union以及位运算 我们可知道 通过 &运算符 可以找到对应的union中所指向位数包含的信息
# define ISA_MASK 0x0000000ffffffff8ULL # define ISA_MAGIC_MASK 0x000003f000000001ULL # define ISA_MAGIC_VALUE 0x000001a000000001ULL union isa_t { Class cls; uintptr_t bits; 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 } }; 复制代码
从上面代码我们可以知道 ISA_MASK的值为0x0000000ffffffff8ULL 所对应的取值就是isa bits 中的shiftcls所在的字节 所以 这里面才是指向class 或者meta-class的地址
- 所以我们有一下结论
arm64之前直接指向class 或者是meta-class arm64之后 isa & ISA_MASK 为class 或者meta-class 地址。isa为 union 结构,用位域来存储更多信息 union 共用体 公用一个内存