- 当我们要了解一个东西的时候,往往会带着疑问,那么今天我们了解下传说中的
isa
,当然也要带着疑问:isa
是什么?存了啥?啥结构? - OK,那我们开始探索了!
1. 准备工作
在探索isa之前。我来开一波上帝视角:要先知道
联合体
与位域
(isa有用到)。
- 在日常iOS开发过程中,基本是不会用到的。所以大多数人不了解。这也给探索底层增加了困难。
我会简单的阐述下、如果想知道深入了解的话我写了另一篇博客:联合体与位域
希望对大家有帮助吧
1.1. 联合体与位域
联合体
所有成员共用一块内存,牵一而动全身!,成员之间是互斥的!修改一个成员会影响其他成员!位域
简单理解就是在位
上存储(我文采有限。。慢慢看下去就知道什么意思了,也可以看下联合体与位域 )
1.2. 所需条件
- 准备份底层源码(没有的去Apple Source找下!也有第二手:我还有篇博客😆 objc4源码编译调试)
2. 开始探索
- 准备工作做完之后--开始深入探索!
2.1. 找出isa
- 每个类对象都有
isa
,我们现在不知道这个是什么?所以我们心啊写点代码把这个东西搞错来:
- 每个对象的本质其实也是一个结构体(我会在下面扩展中证明),我们可以找到其中的
isa
.不过isa
中到底存了些啥子呢。。他是怎么关联类的?怎么存的?接下来开始探索过程。。
2.2. 追寻isa创建的地方
-
我们设置断点。一步一步探索如图三所示:
-
最终走的方法是
objc_object::initIsa
, 我们主要研究的就是这个东西。 -
这段代码是这样的:
inline void
objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor)
{
ASSERT(!isTaggedPointer());
if (!nonpointer) {
isa = isa_t((uintptr_t)cls);
} else {
ASSERT(!DisableNonpointerIsa);
ASSERT(!cls->instancesRequireRawIsa());
isa_t newisa(0);
#if SUPPORT_INDEXED_ISA
ASSERT(cls->classArrayIndex() > 0);
newisa.bits = ISA_INDEX_MAGIC_VALUE;
// isa.magic is part of ISA_MAGIC_VALUE
// isa.nonpointer is part of ISA_MAGIC_VALUE
newisa.has_cxx_dtor = hasCxxDtor;
newisa.indexcls = (uintptr_t)cls->classArrayIndex();
#else
newisa.bits = ISA_MAGIC_VALUE;
// isa.magic is part of ISA_MAGIC_VALUE
// isa.nonpointer is part of ISA_MAGIC_VALUE
newisa.has_cxx_dtor = hasCxxDtor;
newisa.shiftcls = (uintptr_t)cls >> 3;
#endif
// This write must be performed in a single store in some cases
// (for example when realizing a class because other threads
// may simultaneously try to use the class).
// fixme use atomics here to guarantee single-store and to
// guarantee memory order w.r.t. the class index table
// ...but not too atomic because we don't want to hurt instantiation
isa = newisa;
}
}
-
除系统类之外,所有基本都是
nonpointer
。这个地方你就可以验证了。我们首先要确定,这个cls
是我们要探索的类: -
initIsa
方法中我画了一个流程图,大家先观察下他是如何运行的!
2.3. isa结构剖析
-
我们已经知道
isa
的创建赋值流程。那么我们有其他疑问了:isa
是isa_t
的结构。那么isa_t
是什么?不急,我们点进去看看~ -
兄弟们!上帝视角还是很爽的啊。这不就是
联合体
和位域
吗。 -
当然!又有疑问了:赋值的什么?怎么赋值的?
-
好吧! 我又做了一个图(这图可能没啥意义{我觉得大家都知道的!!})
-
然后,我们知道了
isa
结构,isa
里面存了啥
# 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
- 这就是
isa
的结构了,不过这些东西都是什么呢?
2.4 结构解释
-
nonpointer
:表示是否对 isa 指针开启指针优化 0:纯isa指针,1:不止是类对象地址,isa 中包含了类信息、对象的引用计数等. -
has_assoc
:关联对象标志位,0没有,1存在 -
has_cxx_dtor
: 该对象是否有 C++ 或者 Objc 的析构器,如果有析构函数,则需要做析构逻辑, 如果没有,则可以更快的释放对象 -
shiftcls
:存储类指针的值。开启指针优化的情况下,在 arm64 架构中有 33 位用来存储类指针. -
magic
:用于调试器判断当前对象是真的对象还是没有初始化的空间 -
weakly_referenced
:志对象是否被指向或者曾经指向一个 ARC 的弱变量, 没有弱引用的对象可以更快释放 -
deallocating
:标志对象是否正在释放内存 -
has_sidetable_rc
:当对象引用计数大于 10 时,则需要借用该变量存储进位 -
extra_rc
:当表示该对象的引用计数值,实际上是引用计数值减 1, 例如,如果对象的引用计数为 10,那么 extra_rc 为 9。如果引用计数大于 10, 则需要使用到下面的 has_sidetable_rc。 -
大家也都理解位域了,有个图比较形象(我偷得!):
由于我是Mac运行的,所以走得是x86的,iPhone手机的话会走arm64的,其实都差不多。
2.4. 赋值代码
- 先看赋值代码
newisa.bits = ISA_MAGIC_VALUE;
// isa.magic is part of ISA_MAGIC_VALUE
// isa.nonpointer is part of ISA_MAGIC_VALUE
newisa.has_cxx_dtor = hasCxxDtor;
newisa.shiftcls = (uintptr_t)cls >> 3;
- 好像
shiftcls
就是关键。那我们岂不是要看下这是个什么东西。。而且为啥要右移3位? - 首先我们
po (uintptr_t)cls --> XGPerson
- 这。这TM不就是我们想要的类么。
- 为啥要右移3位呢?解释:上面说的
isa
结构:前三位有nonpointer
,has_assoc
,has_cxx_dtor
,占了3位!!
2.5. 逆推(不要乱想😜)
- 继续断点调试吧!。
- 赋值完成后,我们来看看这个
shiftcls
! - 由于我这个是在
x86
下,那么我就把,obj
的isa
--> 右移3位、左移20位、在右移17位(说明:目的是把除isa
中shiftcls
之外都清零)
大家不要觉得这个位移麻烦,其实就是右移3位,再左移3位(恢复位置),再左移17位(把shiftcls左边清0),再右移17位(恢复位置)
结果:
逆推完成!!
3. 扩展clang
-
clang
.对于大多数iOS开发者应该是不陌生的。不过写博客么。我肯定要说一下子(因为我陌生啊。😭) -
1.
Clang
是一个C语言、C++、Objective-C语言的轻量级编译器。源代码发布于BSD协议下。Clang
将支持其普通lambda表达式、返回类型的简化处理以及更好的处理constexpr关键字 -
2.
Clang是一个由Apple主导编写,基于LLVM的C/C++/Objective-C编译器
-
3.还是直接说怎么用比较靠谱!!我又偷了一波图:
通过第一条命令生成main.cpp
!就可以知道上面说的:对象的本质是个结构体了!!