Runtime:isa原理分析
2020-02-05 本文已影响0人
码小菜
夜景
目录
一,正常存储
二,位运算
三,位域
四,共用体
五,NS_OPTIONS
六,isa_t
七,ISA_MASK
一,正常存储
1,实例代码
// Person
@interface Person : NSObject
@property (nonatomic, assign, getter=isTall) BOOL tall;
@property (nonatomic, assign, getter=isRich) BOOL rich;
@property (nonatomic, assign, getter=isHandsome) BOOL handsome;
@end
@implementation Person
@end
// 使用
Person *person = [Person new];
person.tall = YES;
person.rich = NO;
person.handsome = YES;
NSLog(@"%d---%d---%d", person.isTall, person.isRich, person.isHandsome);
// 打印
1---0---1
2,说明
系统会自动生成三个成员变量来存储这三个属性的值,每个成员变量占一个字节,三个成员变量就需要三个字节,下面我们换一种方式,用一个字节来存储这三个属性的值
二,位运算
1,实例代码
//#define TallMask 0b00000001
//#define RichMask 0b00000010
//#define HandsomeMask 0b00000100
#define TallMask (1<<0)
#define RichMask (1<<1)
#define HandsomeMask (1<<2)
@implementation Person
{
/*
占一个字节,默认值为0b00000000
最后一位存储tall,倒数第二位存储rich,倒数第三位存储handsome
*/
char _tallRichHandsome;
}
- (void)setTall:(BOOL)tall {
if (tall) {
/*
将1存储到最后一位,其他位不变
0000 0000
|0000 0001
---------
0000 0001
*/
_tallRichHandsome |= TallMask;
} else {
/*
将0存储到最后一位,其他位不变
0000 0001
&1111 1110
---------
0000 0000
*/
_tallRichHandsome &= ~TallMask;
}
}
- (BOOL)isTall {
/*
取出最后一位的值,其他位置0
0000 0001
&0000 0001
---------
0000 0001
*/
return !!(_tallRichHandsome & TallMask);
}
- (void)setRich:(BOOL)rich {
if (rich) {
_tallRichHandsome |= RichMask;
} else {
_tallRichHandsome &= ~RichMask;
}
}
- (BOOL)isRich {
return !!(_tallRichHandsome & RichMask);
}
- (void)setHandsome:(BOOL)handsome {
if (handsome) {
_tallRichHandsome |= HandsomeMask;
} else {
_tallRichHandsome &= ~HandsomeMask;
}
}
- (BOOL)isHandsome {
return !!(_tallRichHandsome & HandsomeMask);
}
@end
2,说明
- 与(
&
):两个都是1结果才为1 - 或(
|
):只要一个是1结果就为1 - 取反(
~
):将所有位取反,1变0,0变1 - 两次取反(
!!
):将char
类型转为BOOL
类型
三,位域
1,实例代码
@implementation Person
{
// 占一个字节
struct {
char tall : 1; // 占最后一位
char rich : 1; // 占倒数第二位
char handsome : 1; // 占倒数第三位
} _tallRichHandsome;
}
- (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
2,说明
- 结构体中三个成员虽然都是
char
类型,但并不是各占一个字节,而是各占一位 - 结构体的大小是所有成员大小的总和,本应该只占三位的,但结构体的最小单位是字节,所以就占一个字节
四,共用体
1,实例代码
#define TallMask (1<<0)
#define RichMask (1<<1)
#define HandsomeMask (1<<2)
@implementation Person
{
// 占一个字节
union {
char bits;
// 增加可读性
struct {
char tall : 1;
char rich : 1;
char handsome : 1
};
} _tallRichHandsome;
}
- (void)setTall:(BOOL)tall {
if (tall) {
_tallRichHandsome.bits |= TallMask;
} else {
_tallRichHandsome.bits &= ~TallMask;
}
}
- (BOOL)isTall {
return !!(_tallRichHandsome.bits & TallMask);
}
- (void)setRich:(BOOL)rich {
if (rich) {
_tallRichHandsome.bits |= RichMask;
} else {
_tallRichHandsome.bits &= ~RichMask;
}
}
- (BOOL)isRich {
return !!(_tallRichHandsome.bits & RichMask);
}
- (void)setHandsome:(BOOL)handsome {
if (handsome) {
_tallRichHandsome.bits |= HandsomeMask;
} else {
_tallRichHandsome.bits &= ~HandsomeMask;
}
}
- (BOOL)isHandsome {
return !!(_tallRichHandsome.bits & HandsomeMask);
}
@end
2,说明
-
BOOL
值都存储在bits
中,结构体没有实际意义,只为增加可读性 - 共用体的大小是最大成员的大小,
bits
和结构体都占一个字节,所以共用体也占一个字节
五,NS_OPTIONS
// 每个选项占一位
typedef NS_OPTIONS(NSUInteger, MyOptions) {
MyOptionsNone = 0, // 0b00000000
MyOptionsOne = 1 << 0, // 0b00000001
MyOptionstwo = 1 << 1, // 0b00000010
MyOptionsThree = 1 << 2 // 0b00000100
};
- (void)setOptions:(MyOptions)options {
// 0b00000101 & 0b00000001 = 0b00000001
if (options & MyOptionsOne) {
NSLog(@"options包含MyOptionsOne");
}
// 0b00000101 & 0b00000010 = 0b00000000
if (options & MyOptionstwo) {
NSLog(@"options包含MyOptionstwo");
}
// 0b00000101 & 0b00000100 = 0b00000100
if (options & MyOptionsThree) {
NSLog(@"options包含MyOptionsThree");
}
}
- (void)viewDidLoad {
[super viewDidLoad];
// 0b00000001 | 0b00000100 = 0b00000101
MyOptions options = MyOptionsOne | MyOptionsThree;
[self setOptions:options];
}
// 打印
options包含MyOptionsOne
options包含MyOptionsThree
六,isa_t
1,在arm64之前,isa
就是一个普通的指针,只存储class
对象或meta-class
对象的内存地址
struct objc_object {
Class isa;
};
2,从arm64开始,isa
就不是一个普通的指针了,而是一个共用体,不仅存储了class
对象或meta-class
对象的内存地址,而且还存储了其他更多的信息
struct objc_object {
isa_t isa;
};
union isa_t {
// 占八个字节
uintptr_t bits;
struct {
# if __arm64_ // iOS平台
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 deallocating : 1;
uintptr_t has_sidetable_rc : 1;
uintptr_t extra_rc : 19
# elif __x86_64__ // Mac平台
uintptr_t nonpointer : 1;
uintptr_t has_assoc : 1;
uintptr_t has_cxx_dtor : 1;
uintptr_t shiftcls : 44;
uintptr_t magic : 6;
uintptr_t weakly_referenced : 1;
uintptr_t deallocating : 1;
uintptr_t has_sidetable_rc : 1;
uintptr_t extra_rc : 8
#endif
};
};
3,各位域的含义
nonpointer
0表示
isa
是普通指针;1表示isa
是共用体
has_assoc
是否设置过关联对象
has_cxx_dtor
是否含有C++析构函数
shiftcls
存储
class
对象或meta-class
对象的内存地址
magic
对象是否完成初始化
weakly_referenced
对象是否被弱指针引用过
deallocating
对象是否正在销毁
extra_rc
存储的值是对象的引用计数减1
has_sidetable_rc
0表示引用计数存储在
extra_rc
中;1表示引用计数存储在SideTable
中(当引用计数过大extra_rc
存储不下时)
七,ISA_MASK
1,底层代码
# if __arm64_
/*
从倒数第四位开始有33个1
0000 0000 0000 0000 0000 0000 0000 1111
1111 1111 1111 1111 1111 1111 1111 1000
*/
# define ISA_MASK 0x0000000ffffffff8ULL
# elif __x86_64__
/*
从倒数第四位开始有44个1
0000 0000 0000 0000 0111 1111 1111 1111
1111 1111 1111 1111 1111 1111 1111 1000
*/
# define ISA_MASK 0x00007ffffffffff8ULL
#endif
inline Class objc_object::ISA() {
// 取出class对象或meta-class对象的内存地址
return (Class)(isa.bits & ISA_MASK);
}
2,打印验证(Mac平台)
Class
无法打印isa
的地址,所以用yj_objc_class
代替,它就是objc_class
,只是加了个前缀而已
NSObject *instance = [[NSObject alloc] init];
yj_objc_class *clas = (__bridge yj_objc_class *)[NSObject class];
Class metaClass = object_getClass([NSObject class]);
ISA_MASK