主要讲解 isa 指针/方法的底层结构;
文中使用的 objc4源码是objc-781版本;
runtime 部分一
runtime 部分二
runtime 部分三
1. 首先记录一些基础的知识点
1.1 一个16进制位等于四个二进制的位;
例如
0000(十进制)=0000(二进制)=0(十六进制);
0001(十进制)=0001(二进制)=1(十六进制);
0002(十进制)=0010(二进制)=2(十六进制);
0003(十进制)=0011(二进制)=3(十六进制);
0004(十进制)=0100(二进制)=4(十六进制);
0005(十进制)=0101(二进制)=5(十六进制);
0006(十进制)=0110(二进制)=6(十六进制);
0007(十进制)=0111(二进制)=7(十六进制);
0008(十进制)=1000(二进制)=8(十六进制);
0009(十进制)=1001(二进制)=9(十六进制);
0010(十进制)=1010(二进制)=A(十六进制);
0011(十进制)=1011(二进制)=B(十六进制);
0012(十进制)=1100(二进制)=C(十六进制);
0013(十进制)=1101(二进制)=D(十六进制);
0014(十进制)=1110(二进制)=E(十六进制);
0015(十进制)=1111(二进制)=F(十六进制);
1.2 一些常见的二进制位操作
位运算 &
0001 1000
& 0000 1001
--------------
0000 1000
位运算 |
0001 1000
| 0000 1001
--------------
0001 1001
位运算 ~
~0001 1000
--------------
1110 0111
位的左右移动
0x0000 0001 = 1;
0x0000 0001<<0 = 0x0000 0001
0x0000 0001<<1 = 0x0000 0010
0x0000 0001<<2 = 0x0000 0100
1<<0 = 0x0000 0001
1<<1 = 0x0000 0010
1<<2 = 0x0000 0100
取出一个二进制地址中的某一段如何操作?
例如取出0x10001000 1001
1000 中间的红色那8位;
0x1000 1000 1001 1000
& 0x0000 1111 1111 0000 = 0x0000 1000 0001 0000 = 0x1000 0001 0000
2. 通过共用体只用一个字节存储若干个 BOOL 值;
共用体是一种特殊的数据类型,允许您在相同的内存位置存储不同的数据类型。您可以定义一个带有多成员的共用体,但是任何时候只能有一个成员带有值。共用体提供了一种使用相同的内存位置的有效方式。
通过上述的基本知识和位域的简单知识, 可以设计如下代码;
///.h 的接口定义
NS_ASSUME_NONNULL_BEGIN
///通过共用体和位域的操作实现一个字节存放几个BOOL类型的变量
@interface Person : NSObject
- (void)setBig:(BOOL)big;
- (void)setMiddle:(BOOL)middle;
- (void)setSmall:(BOOL)small;
- (BOOL)big;
- (BOOL)middle;
- (BOOL)small;
@end
NS_ASSUME_NONNULL_END
///.m 的实现
#import "Person.h"
///占用一个字节的共用体
union {
char bits;
///下面结构是为了方便阅读, 并没有实际的意义
struct {
///位域操作, big 只占用一位
char big : 1;
///位域操作, middle 只占用一位
char middle : 1;
///位域操作, small 只占用一位
char small : 1;
};
}AllBool;
///通过位的左移定义掩码
#define BigMask (1<<0)
#define MiddleMask (1<<1)
#define SmallMask (1<<2)
@implementation Person
- (void)setBig:(BOOL)big {
if (big) {
AllBool.bits |= BigMask;
}else {
AllBool.bits &= ~BigMask;
}
}
- (void)setMiddle:(BOOL)middle {
if (middle) {
AllBool.bits |= MiddleMask;
}else {
AllBool.bits &= ~MiddleMask;
}
}
- (void)setSmall:(BOOL)small {
if (small) {
AllBool.bits |= SmallMask;
}else {
AllBool.bits &= ~SmallMask;
}
}
- (BOOL)big {
return !!(AllBool.big & BigMask);
}
- (BOOL)middle {
return !!(AllBool.middle & MiddleMask);
}
- (BOOL)small {
return !!(AllBool.small & SmallMask);
}
@end
3. isa 指针的结构
通过 objc4-781源码可以得到isa
的结构如下;
///底层是一个共用体
union isa_t {
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
Class cls;
uintptr_t bits;
#if defined(ISA_BITFIELD)
struct {
///isa 的位域信息
ISA_BITFIELD; // defined in isa.h
};
#endif
};
===>
///arm64构架
# if __arm64__
# define ISA_MASK 0x0000000ffffffff8ULL
# define ISA_MAGIC_MASK 0x000003f000000001ULL
# define ISA_MAGIC_VALUE 0x000001a000000001ULL
# define ISA_BITFIELD \
uintptr_t nonpointer : 1; \
uintptr_t has_assoc : 1; \
uintptr_t has_cxx_dtor : 1; \
uintptr_t shiftcls : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \
uintptr_t magic : 6; \
uintptr_t weakly_referenced : 1; \
uintptr_t deallocating : 1; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 19
# define RC_ONE (1ULL<<45)
# define RC_HALF (1ULL<<18)
/// __x86_64__构架
# elif __x86_64__
# define ISA_MASK 0x00007ffffffffff8ULL
# define ISA_MAGIC_MASK 0x001f800000000001ULL
# define ISA_MAGIC_VALUE 0x001d800000000001ULL
# define ISA_BITFIELD \
uintptr_t nonpointer : 1; \
uintptr_t has_assoc : 1; \
uintptr_t has_cxx_dtor : 1; \
uintptr_t shiftcls : 44; /*MACH_VM_MAX_ADDRESS 0x7fffffe00000*/ \
uintptr_t magic : 6; \
uintptr_t weakly_referenced : 1; \
uintptr_t deallocating : 1; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 8
# define RC_ONE (1ULL<<56)
# define RC_HALF (1ULL<<7)
# else
# error unknown architecture for packed isa
# endif
真机64位架构简化上述结构为:
union isa_t {
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
Class cls;
uintptr_t bits;
struct {
uintptr_t nonpointer : 1;
uintptr_t has_assoc : 1;
uintptr_t has_cxx_dtor : 1;
uintptr_t shiftcls : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/
uintptr_t magic : 6;
uintptr_t weakly_referenced : 1;
uintptr_t deallocating : 1;
uintptr_t has_sidetable_rc : 1;
uintptr_t extra_rc : 19
}
};
nonpointer
: 0: 代表普通指针, 存储着Class
,Mete-Class
对象的内存地址; 1: 代表优化过, 使用位域存储更多信息;
has_assoc
: 是否设置过关联对象(注意只要设置过即使移出了, 也算是关联过), 如果没有关联对象, 释放时会更快;
has_cxx_dtor
: 是否有C++
的析构函数; 如果没有则释放时更快;
shiftcls
: 存放着Class
或者Meta-Class
的地址(通过将isa
的地址&ISA_MASK
即可得到);
magic
: 用于分别在调试时对象是否已经完成初始化;
weakly_referenced
: 标记是否弱引用指向过, 如果没有则释放时更快;
deallocating
: 标记对象是否正在释放;
has_sidetable_rc
: 用来标记引用计数是否过大不能存放在extra_rc
, 如果是1, 引用计数存放在类的的一个属性SideTable
中;
extra_rc
: 存放引用计数(是减一后的值);
从64位构架开始 isa
的地址并不是直接是类对象
或者元类对象
而是需要& ISA_MASK
才能得到其地址, 在arm64
构架下 ISA_MASK = 0x0000000ffffffff8ULL
;而0x0000000ffffffff8
的二进制为如下
isa&ISAac_MASK
另外我们可以推断出OC中的任何
类对象
和元类对象
的地址, 最后三个位一定是000
; 16进制展示是就是末位一定是0
或者8
;
参考文章和下载链接
测试代码
C 共用体
共用体详解
C 语言结构体位域
位域的操作
二进制的位操作
Apple 一些源码的下载地址
网友评论