isa指针(一)
前面的文章中, 讲到了isa
指针, 不过留了个小尾巴
instance
的isa
指针并不是直接指向类对象的. 而是与上了一个ISA_MASK
的东西, 那这是为什么呢?
试图解开这个谜底, 就先要搞清楚什么是位域和联合体
位域的概念和应用, 我在这篇文章里面有提到:
位域
假设现在有这样一个需求
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface Person : NSObject
// 是否是男性
- (void)setMale:(BOOL)male;
- (BOOL)male;
// 是否是汉族
- (void)setHan:(BOOL)han;
- (BOOL)han;
// 是否成年
- (void)setGrown:(BOOL)grown;
- (BOOL)grown;
@end
NS_ASSUME_NONNULL_END
有一个类要存储三个BOOL
类型的变量, 如果直接用属性, 那就会自动生成三个BOOL
类型的变量, 就是3
个字节, 但我们发现, BOOL
类型, 不是0
, 就是1
, 我根本不需要一个字节去保存, 用一位就够了, 那我三个变量共用1个字节就完全够了, 这时候就能用到联合体
#import "Person.h"
#define kMaleMask (1<<0)
#define kHanMask (1<<1)
#define kGrownMask (1<<2)
union PersonFeatures {
char bits;
struct {
char male :1;
char han :1;
char grown :1;
};
};
@implementation Person {
union PersonFeatures personFeatures;
}
- (void)setMale:(BOOL)male {
if (male) {
personFeatures.bits |= kMaleMask;
}else {
personFeatures.bits &= ~kMaleMask;
}
}
- (BOOL)male {
return personFeatures.bits & kMaleMask;
}
- (void)setHan:(BOOL)han {
if (han) {
personFeatures.bits |= kHanMask;
}else {
personFeatures.bits &= ~kHanMask;
}
}
- (BOOL)han {
return personFeatures.bits & kHanMask;
}
- (void)setGrown:(BOOL)grown {
if (grown) {
personFeatures.bits |= kGrownMask;
}else {
personFeatures.bits &= ~kGrownMask;
}
}
- (BOOL)grown {
return personFeatures.bits & kGrownMask;
}
@end
在下面这个联合体中
union PersonFeatures {
char bits;
struct {
char male :1;
char han :1;
char grown :1;
};
};
其实这个结构体并没有什么用, 它只是用来增加可读性的, 当然, 你结构体的位数不能超过你在联合体中定义的那个成员变量所在内存中占用的字节数
struct {
char male :1;
char han :1;
char grown :1;
};
所以事实上, 联合体就是
union PersonFeatures {
char bits;
}
因为我们用的时候, 也只是用了bits
, 用bits
通过一些位运算, 来取出或者存上具体哪一位, 或者哪几位的值.
那我们看回isa
指针
源码中, 一个
struct objc_object
结构体其实只有一个成员变量, 简化过来就是
struct objc_object {
char isa_storage[sizeof(isa_t)];
}
也就是说, struct objc_object
结构体的成员变量是一个char
类型的数组, 这个数组的长度是isa_t
的所占内存空间的大小, 那isa_t
又是个什么玩意儿呢? isa_t
就是我们所说的联合体:
简化下来就是:
union isa_t {
uintptr_t bits;
struct {
uintptr_t nonpointer : 1;
uintptr_t has_assoc : 1;
uintptr_t has_cxx_dtor : 1;
uintptr_t shiftcls : 33;
uintptr_t magic : 6;
uintptr_t weakly_referenced : 1;
uintptr_t unused : 1;
uintptr_t has_sidetable_rc : 1;
uintptr_t extra_rc : 19
};
}
看起来是不是跟我们之前自定义的那种共用体很像了?
所以苹果就是利用这种方式, 去与&
上一个特定的值, 那取到真正的isa
指针, 或者其他的值.
比如: