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的特点:共用一块内存,大小由最长的那个成员决定
    对某一个成员赋值,会影响其他成员的值

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,240评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,328评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,182评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,121评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,135评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,093评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,013评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,854评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,295评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,513评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,398评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,989评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,636评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,657评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容