iOS底层-isa结构(isa_t)

在iOS 底层-- isa指向探究中探索了isa的指向,那么isa的结构具体是什么样的。从源码中来着手研究。

一、位域

在研究isa结构的时候,需要有位域的相关只是因为isa的机构是一个联合体+位域的形式


举个例子:
坦克大战的游戏 中 坦克的方向有上下左右的状态,


常见的写法:为其添加4个变量,

@interface JETank : NSObject

@property (nonatomic, assign) BOOL left;
@property (nonatomic, assign) BOOL right;
@property (nonatomic, assign) BOOL top;
@property (nonatomic, assign) BOOL bottom;

@end

这样可以根据每个变量去拿到相应的状态
但是这里为其分配了多少内存? ------- 8位 (BOOL为1 内存对其 结果为8)


位域的方式

因为用0|1就可以表示具体那个方向,所以我可以定义联合体(union)并且只需要一个char的长度就可以表示4个方向

@interface JETank : NSObject
{
    @public
    union  {
        uintptr_t direction;
        
        struct {
            uintptr_t left   :   1;
            uintptr_t right  :   1;
            uintptr_t top    :   5;  //这里定义为5  只是想说  长度可以根据不同的需求去自定义
            uintptr_t bottom :   1;
        };
    } _jeTankDirection;
}

这样只需要对left/right 等进行相应的赋值就可以满足需求

具体赋值方法

JETank *tank = [JETank new];

/**
方法1:
tank->_jeTankDirection.direction = 0x81;
// 或者 tank->_jeTankDirection.direction = 0b0010 0001;
*/

/**
方法二:
*/
tank->_jeTankDirection.left = YES;  // 为什么可以这样赋值?  因为YES强转之后为1  二进制就是0b1  是满足的
tank->_jeTankDirection.top = 31;   // 这里如果赋值100会报警告(因为top占5位  最大值为 31)
tank->_jeTankDirection.bottom = 0b1;  // 二进制方式赋值
    
NSLog(@"left = %@  top = %@  right = %@  bottom = %@",@(tank->_jeTankDirection.left),
          @(tank->_jeTankDirection.top),
          @(tank->_jeTankDirection.right),
          @(tank->_jeTankDirection.bottom));

//打印结果
left = 1  top = 31  right = 0  bottom = 1

如果top 是赋值 100    100二进制为:0b0110 0100  因为只占5为  只取后5位  就是0b00100  最后打印结果为  top = 8

1、联合体的优势
联合体和结构体写法上有些类似,但是注意区分
联合体的所有信息公用一块内存,起到节省内存的作用
2、位域的作用
直观的表达取值范围,可以直接拿到相应的值

二、isa 结构

我们可以从源码中找到相关内容:

在底层的代码中具体为:

inline void 
objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor) 
{ 
    assert(!isTaggedPointer()); 
    ......
}

因为传入的nonpointer 为 true 所以这里的代码可以简化为:

inline void 
objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor) 
{ 
        isa_t newisa(0);

        newisa.bits = ISA_MAGIC_VALUE;   // 对bits进行初始化
        newisa.has_cxx_dtor = hasCxxDtor;   //赋值
        newisa.shiftcls = (uintptr_t)cls >> 3; //赋值  与class 进行关联

        isa = newisa;
}

在这里进行了 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_BITFIELD 在这里是宏定义(根据架构不同,内容不同),可以在进去一层查看具体定义
__x86_64__ pc端、 __arm64__ 手机端64位

这里以arm64为例:

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          : 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进行初始化的时候,对bits有一个默认的赋值,ISA_MAGIC_VALUE 为宏定义,具体为
0x001d800000000001ULL 在系统计算机上转换为二进制就是64字节

默认值二进制

图解为:
(这张图来源网络 并稍作改动,是针对 x86_64 结构,于本文稍有出入 ( shiftcls 只到36位为止,后面依次排)

isa_t结构

bits里面的具体信息分别代表什么意思?

bits是64为字节  struct是位域

nonpointer :1  (在bits的64位字节中   第0个用于nonpointer信息存储)
 表示是否对isa指针开启指针优化;0代表纯isa指针,1代表不止是类对象指针,还包含了类信息、对象的引用计数等;

has_assoc:1  (在bits的64位字节中   第1个用于has_assoc信息存储)
关联对象标志位,0没有,1存在

has_cxx_dtor:1  (在bits的64位字节中   第2个用于has_cxx_dtor信息存储)
 该对象是否有C++或者Objc的析构器,如果有析构函数,则需要做析构逻辑,如果没有,则可以更快的释放对象;

shiftcls:33  (在bits的64位字节中   第3-35用于shiftcls信息存储)
存储类指针的值。开启指针优化的情况下,在arm64架构中有33位用来存储类指针;

magic:6   (在bits的64位字节中   第36-41用于magic信息存储)
 用于调试器判断当前对象是真的对象还是没有初始化的空间;

 weakly_referenced :1  (在bits的64位字节中   第42个用于weakly_referenced信息存储)
标志对象是否被指向或者曾经指向一个ARC的弱变量,没有弱引用的对象可以更快释放;

 deallocating :1
标志对象是否正在释放内存;

 has_sidetable_rc :1
当对象引用计数大于10时,则需要借用该变量存储进位

 extra_rc :19
当表示该对象的引用计数值,实际上是引用计数值减1,例如,如果对象的引用计数为10,那么extra_rc为9.如果引用计数大于10,则需要使用上面提到的has_sidetable_rc。

三、结构体(struct)与联合体(union)

union
1、可以定义多个成员,大小由最大的成员的大小决定。
2、成员共享同一块大小的内存,一次只能使用其中的一个成员。
3、对某一个成员赋值,会覆盖其他成员的值(也不奇怪,因为他们共享一块内存。但前提是成员所占字节数相同,当成员所占字节数不同时只会覆盖相应字节上的值,比如对char成员赋值就不会把整个int成员覆盖掉,因为char只占一个字节,而int占四个字节)
联合体
4、的存放顺序是所有成员都从低地址开始存放的。

  • 简而言之: union的特点:共用一块内存,大小由最长的那个成员决定
    对某一个成员赋值,会影响其他成员的值

结构体
本质上是多个变量集合到一起,多个变量是同时存在的,互不影响。总体的大小是各个变量值所在内存大小的和(由于内存对齐的原则,总体大小总是>=这个和值)。

你可能感兴趣的:(iOS底层-isa结构(isa_t))