iOS底层原理

iOS 底层原理:isa结构分析

2020-09-11  本文已影响0人  旋风猫

在对objc源码实现的探索的过程中,发现了一个很特别的实现,就是isa。isa 是将对象内存空间与 class 之间联结起来的桥梁,而他的实现也很精妙,在有限的存储空间(一个寄存器的存储空间,在 64 位架构为 16 个字节,在 32 位的架构为 8 个字节)里,因此节省了很多内存空间。

首先,我们先了解一下联合体位域

联合体

我们知道结构体(Struct)是一种构造类型或复杂类型,它可以包含多个类型不同的成员。在C语言中,还有另外一种和结构体非常类似的语法,叫做共用体(Union)。共用体有时也被称为联合或者联合体,它的定义格式为:

union 共用体名{
    成员列表
};

结构体和联合体的区别在于:结构体的各个成员会占用不同的内存空间,互相之间没有影响,处于共存关系;而联合体的所有成员占用同一段内存空间,修改一个成员的内容会影响其余所有成员,各个成员之间处于互斥关系。
结构体占用的内存大于等于所有成员占用的内存的总和(成员之间可能会存在缝隙),共用体占用的内存等于最长的成员占用的内存。共用体使用了内存覆盖技术,同一时刻只能保存一个成员的值,如果对新的成员赋值,就会把原来成员的值覆盖掉。
下面我们通过一段代码验证一下:

union MYUnion{
    double d;
    int i;
    char c;
};

struct MYStruct{
    double d;
    int i;
    char c;
};

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        union MYUnion u = {0};
        struct MYStruct s = {0};
        NSLog(@"联合体u内存大小:%lu", sizeof(u));
        NSLog(@"结构体s内存大小:%lu", sizeof(s));
        NSLog(@"MYUnion d = %f, c = %c, i = %d", u.d,u.c,u.i);
        NSLog(@"MYStruct d = %f, c = %c, i = %d", s.d,s.c,s.i);
        u.i = 1;
        s.i = 1;
        NSLog(@"MYUnion d = %f, c = %c, i = %d", u.d,u.c,u.i);
        NSLog(@"MYStruct d = %f, c = %c, i = %d", s.d,s.c,s.i);
        u.c = 'g';
        s.c = 'g';
        NSLog(@"MYUnion d = %f, c = %c, i = %d", u.d,u.c,u.i);
        NSLog(@"MYStruct d = %f, c = %c, i = %d", s.d,s.c,s.i);
        u.d = 9.9;
        s.d = 9.9;
        NSLog(@"MYUnion d = %f, c = %c, i = %d", u.d,u.c,u.i);
        NSLog(@"MYStruct d = %f, c = %c, i = %d", s.d,s.c,s.i);
    }
    return 0;
}

其中,在x86_64的Mac系统环境下:

2020-09-11 11:51:17.505112+0800 PWDebug[60257:2817639] 联合体u内存大小:8
2020-09-11 11:51:17.506228+0800 PWDebug[60257:2817639] 结构体s内存大小:16
2020-09-11 11:51:17.506321+0800 PWDebug[60257:2817639] MYUnion d = 0.000000, c = , i = 0
2020-09-11 11:51:17.506392+0800 PWDebug[60257:2817639] MYStruct d = 0.000000, c = , i = 0
2020-09-11 11:51:17.506454+0800 PWDebug[60257:2817639] MYUnion d = 0.000000, c = �, i = 1
2020-09-11 11:51:17.506521+0800 PWDebug[60257:2817639] MYStruct d = 0.000000, c = , i = 1
2020-09-11 11:51:17.506579+0800 PWDebug[60257:2817639] MYUnion d = 0.000000, c = g, i = 103
2020-09-11 11:51:17.506639+0800 PWDebug[60257:2817639] MYStruct d = 0.000000, c = g, i = 1
2020-09-11 11:51:17.506696+0800 PWDebug[60257:2817639] MYUnion d = 9.900000, c = \315, i = -858993459
2020-09-11 11:51:17.506761+0800 PWDebug[60257:2817639] MYStruct d = 9.900000, c = g, i = 1

很明显看出,联合体u的其中一个成员(i,c,d)被修改后,其余的成员也受到影响。而结构体s的i,c,d三个变量是稳稳坐拥自己的内存空间,不受其他成员变量影响。

位域

什么是位域(概念)?

带有预定义宽度的变量被称为位域
有些数据在存储时并不需要占用一个完整的字节,只需要占用一个或几个二进制位即可。例如开关只有通电和断电两种状态,用0和1表示足以,也就是用一个二进位。正是基于这种考虑,C语言又提供了一种数据结构,叫做“位域”或“位段”。

位域的声明

struct
{
  type [member_name] : width ;
};
元素 描述
type 只能为 int(整型),unsigned int(无符号整型),signed int(有符号整型) 三种类型,决定了如何解释位域的值。
member_name 位域的名称。
width 位域中位的数量。宽度必须小于或等于指定类型的位宽度,而且最小值为1。

下面我们来看一个例子:

struct
{
  unsigned int a;
  unsigned int w;
  unsigned int h;
} Person;
NSLog(@"%lu", sizeof(Person)); // =>  12
struct
{
  unsigned int a : 3;
  unsigned int w : 3;
  unsigned int h : 10;
} Teacher;
NSLog(@"%lu", sizeof(Teacher)); // =>  4
struct
{
  unsigned short a : 3;
  unsigned short w : 3;
  unsigned short h : 10;
} Student;
NSLog(@"%lu", sizeof(Student)); // =>  2

PersonTeacherStudent占用内存大小分别为1242,它们的成员占用内存如下图:

位域内存占用
总结:可想而知,在实际开发过程中,如果我们真的不需要那么多的内存空间的话,使用位域的编程方式会节省一大笔内存。

isa联合体位域

那么现在我们再回到isa的结构实现

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就是联合体位域,其中它有三个成员:clsbits和一个结构体

所以,isa_t就只是占用了8字节的内存空间。
isa_t位域的解释图,图片来自(关联isa

isa和类关联

未完待续...

上一篇 下一篇

猜你喜欢

热点阅读