从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
为什么要用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只是增强可读性。表示每个字节代表的含义。
#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 共用体 公用一个内存