- 要想学习Runtime,首先要了解它底层的一些常用数据结构,比如isa指针
- 在arm64架构之前,isa就是一个普通的指针,存储着Class、Meta-Class对象的内存地址
- 从arm64架构开始,对isa进行了优化,变成了一个共用体(union)结构,还使用位域来存储更多的信息
- 联合体(共同体)union是什么呢?
源码查看
- 找到这个isa 这个不是 class 而是一个isa_t
- 这个isa_t是一个union
- 具体定义在isa.h
- 这些应该就是架构了-不同的架构他的掩码也是不一样的位运算也是不一样的。
isa详解-位域
- 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的类的属性中
为什么这么用呢
-
举例 定义了一个类这个类有三个bool属性
-
类的赋值与取值
-
大小为 16个字节 一个bool一个字节 + isa 8个字节 = 11字节 -> 内存对齐后 16个字节
NSLog(@"%zd", class_getInstanceSize([MJPerson class])); -
但是 bool 只有0和1 0000 0001 或 0000 0000 实际上用到的只要一个二进制位
-
所以设想能不能把这三个bool放到一个字节里去 bool a , bool b , bool c
- 0000 0100 表示 a=YES b=NO c=NO
- 0000 0010 表示 a=NO b=YES c=NO
- 0000 0001 表示 a=NO b=NO c=YES
-
代码
#import <Foundation/Foundation.h>
@interface MJPerson : NSObject
//@property (assign, nonatomic, getter=isTall) BOOL tall;
//@property (assign, nonatomic, getter=isRich) BOOL rich;
//@property (assign, nonatomic, getter=isHansome) BOOL handsome;
- (void)setTall:(BOOL)tall;
- (void)setRich:(BOOL)rich;
- (void)setHandsome:(BOOL)handsome;
- (BOOL)isTall;
- (BOOL)isRich;
- (BOOL)isHandsome;
@end
#import "MJPerson.h"
// &可以用来取出特定的位
// 0000 0111
//&0000 0100
//------
// 0000 0100
// 掩码,一般用来按位与(&)运算的
//#define MJTallMask 1
//#define MJRichMask 2
//#define MJHandsomeMask 4
//#define MJTallMask 0b00000001
//#define MJRichMask 0b00000010
//#define MJHandsomeMask 0b00000100
#define MJTallMask (1<<0)
#define MJRichMask (1<<1)
#define MJHandsomeMask (1<<2)
@interface MJPerson()
{
char _tallRichHansome;
}
@end
@implementation MJPerson
// 0010 1010
//&1111 1101
//----------
// 0010 1000
- (instancetype)init
{
if (self = [super init]) {
_tallRichHansome = 0b00000100;
}
return self;
}
- (void)setTall:(BOOL)tall
{
if (tall) {
_tallRichHansome |= MJTallMask;
} else {
_tallRichHansome &= ~MJTallMask;
}
}
- (BOOL)isTall
{
return !!(_tallRichHansome & MJTallMask);
}
- (void)setRich:(BOOL)rich
{
if (rich) {
_tallRichHansome |= MJRichMask;
} else {
_tallRichHansome &= ~MJRichMask;
}
}
- (BOOL)isRich
{
return !!(_tallRichHansome & MJRichMask);
}
- (void)setHandsome:(BOOL)handsome
{
if (handsome) {
_tallRichHansome |= MJHandsomeMask;
} else {
_tallRichHansome &= ~MJHandsomeMask;
}
}
- (BOOL)isHandsome
{
return !!(_tallRichHansome & MJHandsomeMask);
}
@end
- 自己写set和get
- _tallRichHansome 定义这个用来保存三个变量的值 0b0000 0000 一个字节
取值
- 假设 tall 在 最后 0000 0001
- rich 在 0000 0010
- hansome 在 0000 0100
与 &运算
- 如果两个值都为1结果就为1否则为0
// 0011
//&1111
//-------
// 0011 - 假设我们现在要取出 rich这个值就是倒数第二位的这个1就与上0010(想去出的位为1,其他位为0)结果就是我们们要的位的值就是1或0,其他位的值必定为0。
// 0011
//&0010
//-------
// 0010 - 所以
- 取出tall值就与 0001 -> 2进制->10进制 (= 1) -> (1<<0) 1 左移0 位
- 取出rich值就与 0010 -> 2进制->10进制 (= 2) -> (1<<1) 1 左移1 位
- 取出hansome值就与 0100 -> 2进制->10进制 (= 4) -> (1<<2) 1 左移2 位
- 这里定义的宏定义就是这个意思了
- 假设 这个存放的值为 0000 0100
- 外部调用了get方法取出这个1 (_tallRichHansome & MJHandsomeMask)
// 0000 0100
//&0000 0100
//-----------
// 0000 0100 - 计算结果为4结果不为0。
- 不为0 : 4!= 0 -> 0! = 1 -> YES 4取反后为0,0取反后为1,1转bool为yes
- 为0 双取反后为0 转bool 为 NO
- 其他两个同理
设值
person.handsome = YES;
person.rich = YES;
person.tall = NO;
- 外部调用set方法设值2个值为YES,一个为NO 里面的 _tallRichHansome 应该为 0000 0110;
| 或运算
- 或:只要其中一个为1就为1
// 0000 0000
//|0000 0110
//----------
// 0000 0110 - 查看上面的计算就知道了设值为YES的时候用或
- handsome 设值为YES的时候就或上一个4
- rich 设值为YES的时候就或上一个2
- tall 设值为YES的时候就或上一个1
设值为NO的时候
// 0000 0010
//&1111 1101
//----------
// 0000 0000
- 设值为NO的时候还是用&
- handsome 设值为NO的时候就与 1111 1011
- rich 设值为NO的时候就与 1111 1101
- tall 设值NO的时候就与 1111 1110
- 掩码取反后与运算 “~”按位取反
优化
- 源码
#import <Foundation/Foundation.h>
@interface MJPerson : NSObject
//@property (assign, nonatomic, getter=isTall) BOOL tall;
//@property (assign, nonatomic, getter=isRich) BOOL rich;
//@property (assign, nonatomic, getter=isHansome) BOOL handsome;
- (void)setTall:(BOOL)tall;
- (void)setRich:(BOOL)rich;
- (void)setHandsome:(BOOL)handsome;
- (void)setThin:(BOOL)thin;
- (BOOL)isTall;
- (BOOL)isRich;
- (BOOL)isHandsome;
- (BOOL)isThin;
@end
#import "MJPerson.h"
#define MJTallMask (1<<0)
#define MJRichMask (1<<1)
#define MJHandsomeMask (1<<2)
#define MJThinMask (1<<3)
@interface MJPerson()
{
union {
int bits;
struct {
char tall : 4;
char rich : 4;
char handsome : 4;
char thin : 4;
};
} _tallRichHandsome;
}
@end
@implementation MJPerson
- (void)setTall:(BOOL)tall
{
if (tall) {
_tallRichHandsome.bits |= MJTallMask;
} else {
_tallRichHandsome.bits &= ~MJTallMask;
}
}
- (BOOL)isTall
{
return !!(_tallRichHandsome.bits & MJTallMask);
}
- (void)setRich:(BOOL)rich
{
if (rich) {
_tallRichHandsome.bits |= MJRichMask;
} else {
_tallRichHandsome.bits &= ~MJRichMask;
}
}
- (BOOL)isRich
{
return !!(_tallRichHandsome.bits & MJRichMask);
}
- (void)setHandsome:(BOOL)handsome
{
if (handsome) {
_tallRichHandsome.bits |= MJHandsomeMask;
} else {
_tallRichHandsome.bits &= ~MJHandsomeMask;
}
}
- (BOOL)isHandsome
{
return !!(_tallRichHandsome.bits & MJHandsomeMask);
}
- (void)setThin:(BOOL)thin
{
if (thin) {
_tallRichHandsome.bits |= MJThinMask;
} else {
_tallRichHandsome.bits &= ~MJThinMask;
}
}
- (BOOL)isThin
{
return !!(_tallRichHandsome.bits & MJThinMask);
}
@end
- char tall : 1;
- :1 表示只占用1位
- 这个结构体只是为了代码可读性并没有实际用处,真正作用的是int bits;int 占四个字节 0x0000 0000
-
得出结论isa指针真正有用的就是这个bits
-
所以我们知道了这个isa指针的8个字节中可以通过掩码找到很多的信息
-
比如这个掩码
-
bits与上这个掩码 就是能找到这些位上的数据了是1还是0呢就知道了
-
得知后三位为零所以isa&上掩码-> 类对象和元类对象的地址后三位一定为0
-
类或元类的地址是后1位就只能是8或0
位运算后的枚举拓展
typedef enum {
// MJOptionsNone = 0, // 0b0000
MJOptionsOne = 1<<0, // 0b0001
MJOptionsTwo = 1<<1, // 0b0010
MJOptionsThree = 1<<2, // 0b0100
MJOptionsFour = 1<<3 // 0b1000
} MJOptions;
- (void)setOptions:(MJOptions)options
{
if (options & MJOptionsOne) {
NSLog(@"包含了MJOptionsOne");
}
if (options & MJOptionsTwo) {
NSLog(@"包含了MJOptionsTwo");
}
if (options & MJOptionsThree) {
NSLog(@"包含了MJOptionsThree");
}
if (options & MJOptionsFour) {
NSLog(@"包含了MJOptionsFour");
}
}
// [self setOptions: MJOptionsOne | MJOptionsFour];
// [self setOptions: MJOptionsOne + MJOptionsTwo + MJOptionsFour];
// self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleTopMargin;
//
// NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
//
// [self addObserver:self forKeyPath:@"age" options:options context:NULL];