一、回顾
oc对象的本质的结构体,结构体内部存放了一个isa指针
//OC
@interface NSObject{
Class isa; // 包含一个isa指针
}
// 对应的 C
struct NSObject_IMPL{
Class isa;
}
// isa 本质就是一个指向 objc_class 结构体的指针
typedef struct objc_class *Class;
typedef struct objc_object *id; // id万能指针的原因
objc_class继承objc_object
二、源码分析
查看runtime源码发现 内部的isa是isa_t类型的
struct objc_object {
private:
isa_t isa; // 此时的isa是isa_t类型的
public:
// ISA() assumes this is NOT a tagged pointer object
Class ISA();
// getIsa() allows this to be a tagged pointer object
Class getIsa();
......
}
isa的作用与内部结构(上)文中提到isa & 一个掩码值之后,才获取到真正的对象的地址,说明isa不是一个单纯的十六进制数据,isa_t类型的isa就是我们真正要找的isa的类型
isa_t的内部结构
union isa_t { //union共用体
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
Class cls;
uintptr_t bits;
#if defined(ISA_BITFIELD)
struct {
ISA_BITFIELD; // defined in isa.h
};
#endif
};
ISA_BITFIELD 内部定义了很多成员
#if SUPPORT_PACKED_ISA
// 真机
# 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;
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)
// 模拟器
# 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;
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
整合到一起阅读
union isa_t { //union共用体
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
Class cls;
uintptr_t bits; // 用于存放所有的数据(struct中的变量)
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 deallocating : 1;
uintptr_t has_sidetable_rc : 1;
uintptr_t extra_rc : 19
}
// nonpointer占1位、has_assoc占1位indexclas占15位
// 他们是按位计算,在内存中一个字节=8位
};
union 是c语言中的共用体,它和结构体有非常类似的用法
结构体和共用体的区别在于:结构体的各个成员会占用不同的内存,互相之间没有影响;而共用体的所有成员占用同一段内存,修改一个成员会影响其余所有成员。
获取共用体中的值可以通过位运算
按位与 & 同为1值为1,否则为0
按位或 | 同为1或1和0的时候,值为1,否则为0
按位取反 ~ 1变为0,0变为1
000111
&111111 通过按位与的运算,我们可以获取特定位置的值
-------
000111
如 00110101,我们获取第三位是什么值
00110101
&00000100 得出的结果为1
00000100
由此可见,通过一个固定的掩码值,我们就可以获取到共用体内指定位置中的值。
//扩展定义,看源码的时候,会遇到这种情况
1<<0 表示将1向左移0位 000001 》000001
1<<1 表示将1向左移1位 000001 》000010
1<<2 表示将1向左移2位 000001 》000100
基本应用:如我们的控制器中有很多个BOOL变量的属性,我们就可以通过一个共用体来存放,获取不同值的时候,使用不同的掩码值,从而达到节省内存的作用
一个BOOL变零占4个字节,而在共用体内只占了1位,具体的使用不做细节描述,不是本文的 主要内容。抽时间专门写一篇文章作为补充
三、属性
// 获取内存地址
isa_t中 有一个shiftcls 长度为33位, 存放的就是地址 通过&一个掩码值,就可以获取shiftcls存放的值。
各个属性的作用
nonpointer :
0,代表普通的指针,存储着Class、Meta-Class对象的内存地址
1,代表优化过,使用位域存储更多的信息
has_assoc:是否有设置过关联对象,如果没有,释放时会更快
has_cxx_dtor:是否有C++的析构函数(.cxx_destruct),如果没有,释放时会更快
shiftcls:存储着Class、Meta-Class对象的内存地址信息
magic:用于在调试时分辨对象是否未完成初始化
weakly_referenced:是否有被弱引用指向过,如果没有,释放时会更快
deallocating:对象是否正在释放
has_sidetable_rc:引用计数器是否过大无法存储在isa中,如果为1,那么引用计数会存储在一个叫SideTable的类的属性中
extra_rc:里面存储的值是引用计数器减1
注意:对象的引用计数器是存储在isa中,extra_rc 占19位,最大数255,如果过大,就无法继续存储,extra_rc减半,另一部分就存放到一个叫SideTable的类中
四、总结
1.isa不是一个普通的指针,可以分为指针型isa和非指针型isa
2.指针型isa,isa的值代表Class的地址
3.非指针型isa,isa的部分值代表Class的地址
4.isa在内存是实际是 isa_t的一个共用体,内部存储着对象的很多信息
思考:isa指向的objc_class 是c/c++的结构体,它包含了NSObject对象的所有信息,objc_class 内部结构是什么样的?
isa的作用与内部结构(上)