@qidiandasheng
2020-10-21T19:12:45.000000Z
字数 4480
阅读 1267
iOS运行时
ARM64位架构之前,isa是一个指针,指向class/meta-class
对象的地址
ARM64位架构开始,isa是一个联合体/共用体(union),这是苹果对isa的优化,结合位域的概念以及位运算的方式来存储更多类相关信息,简单来说就是isa指针通过一个叫ISA_MASK
的值进行二进制&运算,得到真实的class/meta-class
对象的真实地址
64位架构之前:
/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;
/// Represents an instance of a class.
struct objc_object {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
};
64位架构之后:
struct objc_object {
private:
isa_t isa;
public:
// ISA() assumes this is NOT a tagged pointer object
Class ISA();
// rawISA() assumes this is NOT a tagged pointer object or a non pointer ISA
Class rawISA();
.
.
.
void initIsa(Class cls /*nonpointer=false*/);
void initClassIsa(Class cls /*nonpointer=maybe*/);
void initProtocolIsa(Class cls /*nonpointer=maybe*/);
void initInstanceIsa(Class cls, bool hasCxxDtor);
.
.
.
.
}
64位架构开始,不再是一个普通的isa指针了,而是isa_t
,这个东西进一步查看发现
union isa_t {
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
};
# 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)
# 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
// SUPPORT_PACKED_ISA
#endif
ISA_BITFIELD
在x86_64
和arm64
中拥有不同的值,我们可以从下图看出他们的区别:
union的定义是它使几个不同类型的变量共占一段内存(相互覆盖),每次只有一个能使用。 也就是说其中的 isa_t
、cls
、 bits
还有结构体
共用同一块地址空间。而 isa 总共会占据 64 位的内存空间(决定于其中的结构体)
0 表示 raw isa
,也就是没有结构体的部分,访问对象的 isa 会直接返回一个指向 cls 的指针,也就是64 位系统之前时 isa 的类型:
union isa_t {
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
Class cls;
uintptr_t bits;
};
1 表示当前 isa 不是指针,但是其中也有 cls 的信息,只是其中关于类的指针都是保存在 shiftcls
中:
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 : 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;
};
};
对象含有或者曾经含有关联引用,没有关联引用的可以更快地释放内存
表示该对象是否有 C++ 或者 Objc 的析构器
类的指针。arm64架构中有33位可以存储类指针。
判断对象是否初始化完成,在arm64中0x16是调试器判断当前对象是真的对象还是没有初始化的空间。
对象被指向或者曾经指向一个 ARC 的弱变量,没有弱引用的对象可以更快释放
对象是否正在释放内存
判断该对象的引用计数是否过大,如果过大则需要其他散列表来进行存储。
存放该对象的引用计数值减一后的结果。对象的引用计数超过 1,会存在这个这个里面,如果引用计数为 10,extra_rc的值就为 9。
注:以下以x86_64
为例
inline void
objc_object::initIsa(Class cls)
{
initIsa(cls, false, false);
}
//这里对方法做了简化,留下重要的部分
inline void
objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor)
{
isa.bits = ISA_MAGIC_VALUE;
isa.has_cxx_dtor = hasCxxDtor;
isa.shiftcls = (uintptr_t)cls >> 3;
}
第一个参数nonpointer
表示是否开启指针优化。nonpointer
=true,表示开启。
这就是Tagged Pointer
技术的一种使用。在64位系统中,一个指针的大小将是64位(8字节)。使用这种技术之后我们可以在一个指针中存储一些信息,节省内存空间。
对整个 isa 的值 bits 进行设置,传入 ISA_MAGIC_VALUE
:
#define ISA_MAGIC_VALUE 0x001d800000000001ULL
我们可以把它转换成二进制的数据11101100000000000000000000000000000000000000000000001
(一共53位),然后看一下哪些属性对应的位被这行代码初始化了(标记为红色):
从图中了解到,在使用 ISA_MAGIC_VALUE
设置 isa_t
结构体之后,实际上只是设置了 nonpointer
以及 magic
这两部分的值。
在设置 nonpointer
和 magic
值之后,会设置 isa 的 has_cxx_dtor
,这一位表示当前对象有 C++ 或者 ObjC 的析构器(destructor),如果没有析构器就会快速释放内存。
isa.has_cxx_dtor = hasCxxDtor;
isa.shiftcls = (uintptr_t)cls >> 3;
将当前地址右移三位的主要原因是用于将 Class 指针中无用的后三位清除减小内存的消耗,因为类的指针要按照字节(8 bits)对齐内存,其指针后三位都是没有意义的 0。
uintptr_t bits;
这个东西类型是unsigned long
类型,表达的是类的信息。苹果结合了位域的概念和位运算来做的优化,也就是将结构体中的成员通过位域这种数据结构分配了各个成员的内存,达到节约内存的目的,再通过各种MASK跟bits进行&运算,得到想要的信息,具体想要什么信息就拿对应的MASK去运算。
# define ISA_MASK 0x00007ffffffffff8ULL
十六进制转换为二进制为11111111111111111111111111111111111111111111000
(47位),通过bits
和ISA_MASK
位与运算得到isa类指针,也就是说除去后面3个0,和bits
的第4位开始的44个数与运算得到indexcls
:
inline Class
objc_object::ISA()
{
//非TaggedPointer类型继续往下走
ASSERT(!isTaggedPointer());
#if SUPPORT_INDEXED_ISA
if (isa.nonpointer) {
uintptr_t slot = isa.indexcls;
return classForIndex((unsigned)slot);
}
return (Class)isa.bits;
#else
return (Class)(isa.bits & ISA_MASK);
#endif
}
Runtime入院第一天—— isa 和 Class
iOS---关于isa
从 NSObject 的初始化了解 isa