在arm64架构之前,isa就是一个普通的指针,存储着Class、Meta-Class对象的内存地址;
从arm64架构开始,对isa进行了优化,变成了一个共用体 "union" 结构,还使用位域来存储更多的信息。
所以,为什么要使用这种方式来优化呢?
- 首先我们看下64位系统下isa指针的结构
- 结论:使用共用体 union,可以在有限的空间内,同样是8个字节,我们可以存储更多的信息
我们以Person对象为例,来逐步了解 isa 使用 union 的优化方式;
1. 普通的Person对象
我们以Person这个对象为例:假设它有3个成员变量,tail、rich、handsome,那么正常情况下它需要占用16个字节;
@interface Person : NSObject
@property (nonatomic, assign) BOOL tall;
@property (nonatomic, assign) BOOL rich;
@property (nonatomic, assign) BOOL handsome;
@end
3个成员变量占用3个字节,isa指针占用8个字节,所以一共占用11个字节,内存对齐后,总共占用16个字节
NSLog(@"正常的 Person 对象占用:%zd 个字节",class_getInstanceSize([Person class]));
// 输出:正常的 Person 对象占用:16 个字节
2. 以位运算的方式优化
1个成员变量_tallRichHandsome 占用1个字节,isa指针8个字节,内存对其后,总共占用16个字节;
-
我们不声明其成员变量,直接创建对应的方法;
@interface Person : NSObject - (void)setTall:(BOOL)tall; - (void)setRich:(BOOL)rich; - (void)setHandsom:(BOOL)handsome; - (BOOL)isTall; - (BOOL)isRich; - (BOOL)isHandsome; @end
-
声明一个char类型的对象,用来按位存储tall、rich、handsome
@interface Person(){ char _tallRichHandsome; // 0b 0000 0111 ; 占1个字节 } @end
-
通过位运算,实现set、get方法
#define TallMask (1<<0) // 0000 0001 (掩码) #define RichMask (1<<1) // 0000 0010 #define HandsomeMask (1<<2) // 0000 0100 @interface Person(){ char _tallRichHandsome; // 0b 0000 0111 ; 占1个字节 } @end @implementation Person - (void)setTall:(BOOL)tall{ if (tall) { _tallRichHandsome |= TallMask; // 按位或 } else { _tallRichHandsome &= ~TallMask; // 取反后按位与 } } - (void)setRich:(BOOL)rich{ // 同setTall方法... } - (void)setHandsome:(BOOL)handsome{ // 同setTall方法... } // 跟掩码进行与运算,再取2次反后,结果即为期待值(只要大于0,则为YES) - (BOOL)isTall{ return !!(_tallRichHandsome & TallMask); } - (BOOL)isRich{ return !!(_tallRichHandsome & RichMask); } - (BOOL)isHandsome{ return !!(_tallRichHandsome & HandsomeMask); } @end
3. 利用结构体继续优化 (位域)
-
利用结构体 代替 char类型的 _tallRichHandsome,然后利用位域,保证_tallRichHandsome仅占1个字节(不然的话,每个char都占1个字节,一共又占用3个字节了);
@interface Person(){ struct { char tall : 1; // :1 位域,代表只占1位 char rich : 1; char handsome : 1; } _tallRichHandsome; // 按照先后顺序从右向左排 0b 0000 0000 } @end @implementation Person - (void)setTall:(BOOL)tall{ _tallRichHandsome.tall = tall; } - (void)setRich:(BOOL)rich{ _tallRichHandsome.rich = rich; } - (void)setHandsome:(BOOL)handsome{ _tallRichHandsome.handsome = handsome; } - (BOOL)isTall{ return _tallRichHandsome.tall; } - (BOOL)isRich{ return _tallRichHandsome.rich; } - (BOOL)isHandsome{ return _tallRichHandsome.handsome; } @end
-
此时输出,会发现BOOL输出了-1,打断点发现其为255;
原因是 tall 只占1个二进制位,即为0b1,return tall 的时候,强制转为 8位 的 BOOL 类型,程序会默认按照自己的规则转,结果转成了 0b 1111 1111 (255),最高位为1,所以为 -1 的补码,所以输出了 -1 (减1后得反码,再取反后得原码)Person *person = [[Person alloc] init]; person.tall = YES; person.rich = YES; person.handsome = NO; NSLog(@"tall: %d ; rich: %d ; handsome: %d",person.isTall,person.isRich,person.isHandsome); // tall: -1 ; rich: -1 ; handsome: 0 BOOL isTall = person.isTall; // 255
解决方式为:
- (BOOL)isTall{ return !!_tallRichHandsome.tall; // 取值的时候,2次取反即可 }
4. 用 union 共用体 优化
因为共用体内共用同一块内存,所以 bits 和 struct 结构体 共用 他们最大的内存:1个字节;实际上和 "2. 以位运算的方式优化" 是一样的
@interface Person(){
union { // 共用体
char bits;
struct { // 增加可读性,无任何作用,删除也不影响结果
char tall : 1;
char rich : 1;
char handsome : 1;
};
} _tallRichHandsome;
}
@end
@implementation Person
- (void)setTall:(BOOL)tall{
if (tall) {
_tallRichHandsome.bits |= TallMask;
} else {
_tallRichHandsome.bits &= ~TallMask;
}
}
- (void)setRich:(BOOL)rich{
// 同setTall方法...
}
- (void)setHandsome:(BOOL)handsome{
// 同setTall方法...
}
- (BOOL)isTall{
return !!(_tallRichHandsome.bits & TallMask);
}
- (BOOL)isRich{
return !!(_tallRichHandsome.bits & RichMask);
}
- (BOOL)isHandsome{
return !!(_tallRichHandsome.bits & HandsomeMask);
}
@end
阅读量
loading...