在笔者编写的iOS-Objective-C的本质中,我们已经了解到,所有的OC对象本质中都包含了一个isa指针,这个指针要么指向类对象,要么指向元类对象,今天,我们进一步来观察下这个isa指针内部所包含的信息。
既然要观察isa内部的信息,我们当然得去查看objc的源码,因为大家都知道isa底层的源码在Xcode中是无法看到的,笔者查看了一下最新的objc源码最新到了objc4-756.2.tar.gz。
下载之后,我们一起看下isa的底层结构吧。
首先我们在Xcode中搜索下isa,第一个搜索到的就是
Class isa OBJC_ISA_AVAILABILITY;
然后点击Class,看到的是
typedef struct objc_class *Class;
typedef struct objc_object *id;
点击objc_class,发现它是一个结构体,而且继承objc_object
struct objc_class : objc_object {
......
}
那我们就直接看objc_object:
struct objc_object {
private:
isa_t isa;
public:
......
}
这里我们就发现了我们需要查找的目标isa_t 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
};
由此可见,isa_t是一个union,简称共用体。而且在这个地方有个if判断,如果定义了ISA_BITFIELD,则包含一个结构体。搜索一下ISA_BITFIELD,可以找到
# 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_BITFIELD这个结构体,这个结构体中用到的知识点就是位域,内容就是紧跟着它下面的那个部分,所以在arm64下,isa_t的整体结构如下:
union isa_t {
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
Class cls;
uintptr_t bits;
struct {
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
};
};
到这里,我们已经可以看到,OC底层在定义isa的时候,需要用到的知识点是union、位域,那所谓的位运算又在哪里呢?其实就是跟arm64系统下定义的以下几个宏有关系
# define ISA_MASK 0x0000000ffffffff8ULL
# define ISA_MAGIC_MASK 0x000003f000000001ULL
# define ISA_MAGIC_VALUE 0x000001a000000001ULL
了解OC本质的同学可能知道,其实在很早之前,OC对象底层结构体的isa指针,是直接指向这个对象对应的类对象或者元类对象的地址,不过在之后的版本中,苹果针对isa指针做了优化,在得到对象的isa指针地址值之后需要&上一个ISA_MASK,才能真正得到这个对象对应的类对象或者元类对象的地址值。所以这里就涉及到了位运算,相信位运算大家都了解,这也是C语言的基础知识,总结的来说就是
&与运算,任何值 &1就是本身,&0就是0
| 或运算,任何值 | 1就是1,| 0就是本身
1 & 1 => 1 1 | 1 => 1
1 & 0 => 0 0 | 1 => 1
0 & 0 => 0 0 | 0 => 0
接下来就是位域的知识点,其实很简单,就比方说上述所定义:
struct {
uintptr_t nonpointer : 1; /*位域*/
uintptr_t has_assoc : 1;
uintptr_t has_cxx_dtor : 1;
......
};
每一个变量占有一位,那这么做的意义在哪里呢?其实就是可以节省内存空间,大家想想,如果我们拿一个bool类型来表示一个标志位,这样的话,每一个标志位都需要4个字节,那如果我们拿一个字节中的一个位来表示这个标志位,不也是可行的吗?因为本身一个位就是用1和0表示,不就刚好代表着true和false吗,就如以下这个定义:
struct {
char sex : 1; /*性别,1表示男,0表示女*/
char isLikeMike : 1; /*是否喜欢牛奶*/
char isThin : 1; /*是否偏瘦*/
char handsome : 1; /*是否帅气*/
} _personCharacteristic; /*个人特征*/
这个时候,我们就可以很大程度上节约的内存空间,因为每个特诊字段,我们都只需要一个位就可以搞定。至于如何取值赋值,这里暂时不做探讨,大家有空可以自己玩玩。
接下来,我们在来看看union这个共用体是什么用法,它的大概意思就是大家一起共用一个空间,先举个简单的例子:
union {
int year;
int month;
int day;
} _yearMonthDay;
int main(int argc, const char * argv[]) {
@autoreleasepool {
_yearMonthDay.year = 2019;
NSLog(@"year is %d -- month is %d -- day is %d", _yearMonthDay.year, _yearMonthDay.month, _yearMonthDay.day);
}
return 0;
}
打印出来的结果就是如下:
year is 2019 -- month is 2019 -- day is 2019
所以从这里,大家应该知道了union的用法。
那最后一个问题就是union和struct一起用,会起到一个什么作用呢?我们回到isa_t的定义上来:
union isa_t {
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
Class cls;
uintptr_t bits;
struct {
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
};
};
我们可以看到isa_t中结构体部分的总大小是64位,uintptr_t这个其实是
typedef unsigned long uintptr_t;
也是64位,所以结构体和bits是大小一致的 这样就符合了我们的共用体规则。这样的话,后面有关于所有结构体内位运算的处理,我们都可以用bits来代替,不需要把结构体中的每一个字段读取出来,而结构体的主要内容,就是使我们的isa_t的定义有很高的可读性。这也就是isa_t将union和struct结合使用的核心意义所在!
还有一个补充的一点就是结构体内的各个字段的意义:
关于Runtime中isa的基本底层结构的分享暂时就到这里,后面笔者会继续分享有关于Runtime底层的其他更多的知识点,希望对大家有所帮助,如果分享的地方有偏差的,欢迎大家一起怒!!!