文章目录
iOS - Runtime-isa详解(位域、union(共用体)、位运算)
前言
本章主要了解Runtime相关内容,苹果对isa
做了哪些优化,位域、union(共用体)又是如何运用的
- 要想学习Runtime,首先要了解它底层的一些常用数据结构,比如isa指针
- 在arm64架构之前,isa就是一个普通的指针,存储着
Class
、Meta-Class
对象的内存地址 - 从arm64架构开始,对isa进行了优化,变成了一个
共用体(union)
结构,还使用位域
来存储更多的信息
1. 位域
介绍
利用好位域
的话,可以使程序运行高效
、节省内存
,操作系统级别的东西很多都会使用
在IM系统的开发中,就使用了
位域
来对数据进行优化,后续有时间再抽出案例聊聊。
欢迎点赞,收藏,加关注!,谢谢你!!
1.1 思路
假如ZSXPerson
类需要3个BOOL类型的属性,这时候我们通常会直接使用@property
声明3个属性,这时候系统会给我们生成 3个_
开头的成员变量,3对get
、set
方法,所占据的内存也比较多
思路: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, // 0b00000001
ZSXOptions2 = 1 << 1, // 0b00000010
ZSXOptions3 = 1 << 2, // 0b00000100
ZSXOptions4 = 1 << 3, // 0b00001000
ZSXOptions5 = 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 原理分析
/**
0b00000001
0b00000100
0b00010000
----------------- | 运算(设置值)
0b00010101
0b00000001
----------------- & 运算(取值)
0b00000001 为 true
*/
标签:person,union,iOS,ZSXPerson,BOOL,位域,isa,Class From: https://blog.csdn.net/sharp521/article/details/137010961@oubijiexi