当前位置: 首页 > news >正文

iOS - Runtime-isa详解(位域、union(共用体)、位运算)

文章目录

  • iOS - Runtime-isa详解(位域、union(共用体)、位运算)
    • 前言
    • 1. `位域`介绍
      • 1.1 思路
      • 1.2 示例 - 结构体
      • 1.3 示例 - union(共用体)
        • 1.3.1 说明
      • 1.4 结构体 对比 union(共用体)
    • 2. arm64架构对isa的优化
      • 2.1 位域内容
        • nonpointer
        • has_assoc
        • has_cxx_dtor
        • shiftcls
        • magic
        • weakly_referenced
        • deallocating
        • extra_rc
        • has_sidetable_rc
      • 2.2 Class、Meta-Class对象存储位置
    • 3. 拓展
      • 3.1 枚举值设计
      • 3.1.1 案例
      • 3.1.2 原理分析

iOS - Runtime-isa详解(位域、union(共用体)、位运算)

前言

本章主要了解Runtime相关内容,苹果对isa做了哪些优化,位域、union(共用体)又是如何运用的

  • 要想学习Runtime,首先要了解它底层的一些常用数据结构,比如isa指针
  • 在arm64架构之前,isa就是一个普通的指针,存储着ClassMeta-Class对象的内存地址
  • 从arm64架构开始,对isa进行了优化,变成了一个共用体(union)结构,还使用位域来存储更多的信息

1. 位域介绍

利用好位域的话,可以使程序运行高效节省内存,操作系统级别的东西很多都会使用

在IM系统的开发中,就使用了位域来对数据进行优化,后续有时间再抽出案例聊聊。

欢迎点赞,收藏,加关注!,谢谢你!!

1.1 思路

假如ZSXPerson类需要3个BOOL类型的属性,这时候我们通常会直接使用@property声明3个属性,这时候系统会给我们生成 3个_开头的成员变量,3对getset方法,所占据的内存也比较多

思路:BOOL 类型属性值要么 YES 要么 NO,使用字节中的一个(0或者1)其实就能表示一个 BOOL 类型的属性值,一个字节就可以表示8个 BOOL 值

1.2 示例 - 结构体

ZSXPerson.h

@interface ZSXPerson : NSObject- (void)setTall:(BOOL)tall;- (BOOL)isTall;- (void)setRich:(BOOL)rich;- (BOOL)isRich;- (void)setHandsome:(BOOL)handsome;- (BOOL)isHandsome;@end

ZSXPerson.m

@interface ZSXPerson() {struct {char tall: 1;char rich: 1;char handsome: 1;} _tallRichHandsome;
}@end@implementation ZSXPerson- (void)setTall:(BOOL)tall {_tallRichHandsome.tall = tall;
}- (BOOL)isTall {return !!_tallRichHandsome.tall;
}- (void)setRich:(BOOL)rich {_tallRichHandsome.rich = rich;
}- (BOOL)isRich {return !!_tallRichHandsome.rich;
}- (void)setHandsome:(BOOL)handsome {_tallRichHandsome.handsome = handsome;
}- (BOOL)isHandsome {return !!_tallRichHandsome.handsome;
}@end

main.m

int main(int argc, const char * argv[]) {@autoreleasepool {ZSXPerson *person = [[ZSXPerson alloc] init];person.tall = NO;person.rich = YES;person.handsome = YES;NSLog(@"tall:%d  rich:%d  handsome:%d", person.isTall, person.isRich, person.isHandsome);}return 0;
}

运行结果:

1.3 示例 - union(共用体)

ZSXPerson.h

@interface ZSXPerson : NSObject- (void)setTall:(BOOL)tall;- (BOOL)isTall;- (void)setRich:(BOOL)rich;- (BOOL)isRich;- (void)setHandsome:(BOOL)handsome;- (BOOL)isHandsome;@end

ZSXPerson.h.m

#import "ZSXPerson.h"#define ZSXTallMask (1)
#define ZSXRichMask (1 << 1)
#define ZSXHandsomeMask (1 << 2)@interface ZSXPerson() {union {char bits;struct {char tall: 1;char rich: 1;char handsome: 1;};}_tallRichHandsome;
}@end@implementation ZSXPerson- (void)setTall:(BOOL)tall {if (tall) {_tallRichHandsome.bits |= ZSXTallMask;}else {_tallRichHandsome.bits &= ~ZSXTallMask;}
}- (BOOL)isTall {return !!(_tallRichHandsome.bits & ZSXTallMask);
}- (void)setRich:(BOOL)rich {if (rich) {_tallRichHandsome.bits |= ZSXRichMask;}else {_tallRichHandsome.bits &= ~ZSXRichMask;}
}- (BOOL)isRich {return !!(_tallRichHandsome.bits & ZSXRichMask);
}- (void)setHandsome:(BOOL)handsome {if (handsome) {_tallRichHandsome.bits |= ZSXHandsomeMask;}else {_tallRichHandsome.bits &= ~ZSXHandsomeMask;}
}- (BOOL)isHandsome {return !!(_tallRichHandsome.bits & ZSXHandsomeMask);
}@end

main.m

int main(int argc, const char * argv[]) {@autoreleasepool {ZSXPerson *person = [[ZSXPerson alloc] init];person.tall = NO;person.rich = YES;person.handsome = YES;NSLog(@"tall:%d  rich:%d  handsome:%d", person.isTall, person.isRich, person.isHandsome);}return 0;
}

运行结果:

1.3.1 说明

1.4 结构体 对比 union(共用体)

  • 结构体的成员是各自占用各自所需大小
  • 共同体的内存大小取决于其中最大的成员的大小,所有成员共用这块内存

  • 使用共用体实际上还是通过位运算来控制每个属性所占位置
  • 其中的sturct目的是增加可读性,实际上不会影响属性所占位置

2. arm64架构对isa的优化

arm64架构对isa中,使用一个64位的共用体来存储更多的信息,通过位域的概念来表示各个存储的信息的存储位置。其中有33位拿来存储Class、Meta-Class地址值


2.1 位域内容

nonpointer
  • 0,代表普通的指针,存储着Class、Meta-Class对象的内存地址
  • 1,代表优化过,使用位域存储更多的信息
has_assoc
  • 是否有设置过关联对象,如果没有,释放时会更快
has_cxx_dtor
  • 是否有C++的析构函数(.cxx_destruct),如果没有,释放时会更快
shiftcls
  • 存储着Class、Meta-Class对象的内存地址信息
magic
  • 用于在调试时分辨对象是否未完成初始化
weakly_referenced
  • 是否有被弱引用指向过,如果没有,释放时会更快
deallocating
  • 对象是否正在释放
extra_rc
  • 里面存储的值是引用计数器减1
has_sidetable_rc
  • 引用计数器是否过大无法存储在isa中
  • 如果为1,那么引用计数会存储在一个叫SideTable的类的属性中

2.2 Class、Meta-Class对象存储位置

Class、Meta-Class对象存储在shiftcls,从上图可知shiftcls是从第4位开始,连续33

isa的掩码:define ISA_MASK 0x0000000ffffffff8ULL

将掩码转为二进制查看

它表示的就是从第4位开始,连续33位,通过这个掩码,就可以将Class、Meta-Class地址值取出来

Class、Meta-Class地址值后三位永远是 0。因为他的掩码左右边 3位是 0,&运算后一定是 0

3. 拓展

3.1 枚举值设计

在iOS中,系统的一些API可以使用|传入多个枚举值,比如:

self.view.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleRightMargin;

原理就是使用位域设计枚举值,然后通过位运算来取值

3.1.1 案例

我们也自己来设计一个这样的枚举

定义枚举:

typedef NS_OPTIONS(NSUInteger, ZSXOptions) {ZSXOptions1                 = 1 << 0, // 0b00000001ZSXOptions2                 = 1 << 1, // 0b00000010ZSXOptions3                 = 1 << 2, // 0b00000100ZSXOptions4                 = 1 << 3, // 0b00001000ZSXOptions5                 = 1 << 4, // 0b00010000
};

设置值方法:

- (void)setOptions:(ZSXOptions)options {if (options & ZSXOptions1) {NSLog(@"包含了ZSXOptions1");}if (options & ZSXOptions2) {NSLog(@"包含了ZSXOptions2");}if (options & ZSXOptions3) {NSLog(@"包含了ZSXOptions3");}if (options & ZSXOptions4) {NSLog(@"包含了ZSXOptions4");}if (options & ZSXOptions5) {NSLog(@"包含了ZSXOptions5");}
}

使用:

[self setOptions:ZSXOptions1 | ZSXOptions3 | ZSXOptions5];

打印如下:

此时我们已经实现了一个可以传入多个枚举值的接口

3.1.2 原理分析

/**0b000000010b000001000b00010000----------------- | 运算(设置值)0b000101010b00000001----------------- & 运算(取值)0b00000001    为 true*/

@oubijiexi

相关文章:

  • 【嵌入式开发 Linux 常用命令系列 1.4 -- grep -Ev 反向搜索】
  • golang 使用redis
  • Sentinel入门
  • Linux文件IO(2):使用标准IO进行文件的打开、关闭、读写、流定位等相关操作
  • 基于Java在线考试系统系统设计与实现(源码+部署文档)
  • 算法---动态规划练习-6(地下城游戏)
  • ​马来语翻译中文去哪比较好?
  • 反序列化动态调用 [NPUCTF2020]ReadlezPHP1
  • Redis 特性,为什么要用Redis,Redis到底是多线程还是单线程
  • 如何使用 ArcGIS Pro 制作三维建筑
  • Spring和Spring Boot之间的区别
  • 非wpf应用程序项目【类库、用户控件库】中使用HandyControl
  • IDEA设置内存大小不生效
  • 二、数据库管理员密码管理
  • CSS及javascript
  • 2019.2.20 c++ 知识梳理
  • 4月23日世界读书日 网络营销论坛推荐《正在爆发的营销革命》
  • gops —— Go 程序诊断分析工具
  • go语言学习初探(一)
  • java B2B2C 源码多租户电子商城系统-Kafka基本使用介绍
  • javascript 总结(常用工具类的封装)
  • nfs客户端进程变D,延伸linux的lock
  • Swift 中的尾递归和蹦床
  • 浮现式设计
  • 关于extract.autodesk.io的一些说明
  • 面试遇到的一些题
  • 适配mpvue平台的的微信小程序日历组件mpvue-calendar
  • 吴恩达Deep Learning课程练习题参考答案——R语言版
  • Python 之网络式编程
  • 函数计算新功能-----支持C#函数
  • ​一文看懂数据清洗:缺失值、异常值和重复值的处理
  • # 再次尝试 连接失败_无线WiFi无法连接到网络怎么办【解决方法】
  • #每天一道面试题# 什么是MySQL的回表查询
  • $L^p$ 调和函数恒为零
  • (C++)八皇后问题
  • (Mirage系列之二)VMware Horizon Mirage的经典用户用例及真实案例分析
  • (附源码)springboot掌上博客系统 毕业设计063131
  • (更新)A股上市公司华证ESG评级得分稳健性校验ESG得分年均值中位数(2009-2023年.12)
  • (三)Hyperledger Fabric 1.1安装部署-chaincode测试
  • (一)Java算法:二分查找
  • (一)基于IDEA的JAVA基础10
  • (转)项目管理杂谈-我所期望的新人
  • (轉貼) 寄發紅帖基本原則(教育部禮儀司頒布) (雜項)
  • .java 9 找不到符号_java找不到符号
  • .NET CF命令行调试器MDbg入门(四) Attaching to Processes
  • .NET Framework 3.5中序列化成JSON数据及JSON数据的反序列化,以及jQuery的调用JSON
  • .NET/C# 反射的的性能数据,以及高性能开发建议(反射获取 Attribute 和反射调用方法)
  • []常用AT命令解释()
  • [BZOJ3223]文艺平衡树
  • [codeforces]Checkpoints
  • [EFI]ASUS EX-B365M-V5 Gold G5400 CPU电脑 Hackintosh 黑苹果引导文件
  • [FZSZOJ 1223] 上海红茶馆
  • [GN] 设计模式——面向对象设计原则概述
  • [halcon案例2] 足球场的提取和射影变换
  • [IT生活推荐]大家一起来玩游戏喽,来的都进!