一、alloc 过程中的符号表绑定
在第一篇中有贴出过 alloc 实现的整个流程图,alloc -> _objc_rootAlloc -> callAlloc -> ...
但在我们实际运行过中会发现在执行 _objc_rootAlloc 之前会先 objc_alloc 这一方法,在我们对 alloc 进行 Jump to Definition 的时候就直接到了 _objc_rootAlloc 这一方法,所以 objc_alloc 这一层 往往就会被我们忽略跳过了,再往里面走就如注释所描述,就执行[cls alloc]
下载地址:pan.baidu.com/s/1Z71KyxPy… 提取码: picu
就相当于 objc_alloc 绑定到了 alloc 的 sel;换个方式解释就是,一个方法 有先是找到 sel 然后再是 imp。举个例子,字典查字,查找的过程就是 目录 -> 查找到字(sel) -> 根据页码找到字所在位置(imp) -> 字的详细含义等(函数实现)。
然后有个问题,在哪可以看到 sel_alloc 与 objc_alloc 类似绑定的源码呢?(答案看下图)
看第二部分分析
二、objc_alloc (ps:这个过程有点会感觉很飘加无聊,都是正常的😂)
我们先打开汇编环境:Debug -> Debug Workflow -> Always Show Disassembly
这是因为没有走 objc_msgSend 方法的方法都是系统直接调用的符号。
那系统又是什么时候调用的呢?
再次走一下上面的步骤。我们先将允许的tagter 拖到 MacOView 查看 Symbol Table
如果在编译期调用那就不能直接进行探索了,这时就需要借助 LLVM ,因为 objc_alloc 的源码苹果官方并没有公布出来,所以这并不是 objc_alloc 源码,然后只能在 LLVM 里找到一些与 objc_alloc 相关的东西进行分析
LLVM工程获取连接:链接:pan.baidu.com/s/1R-3KSlea… 提取码:mfi8
打开工程后就开始在工程里找带有 objc_alloc 这个关键字的方法
到这里我们来回答几个问题:
1)为什么会走 objc_alloc 这个方法?
因为在底层有个LLVM,在这里面有个Callee函数,调用这个函数会有一个返回,这里有个返回就意味着调用 EmitCallOrInvoke 的返回
现在回答
1)为什么要调用 objc_alloc 创建对象而不是直接调用 alloc 创建就可以了呢? 在 tryGenerateSpecializedMessageSend 这个方法实现的地方有注释我将它贴出来
第一个方框概述大概意思就是: ObjC运行时可能提供的入口点可能比相应选择器的普通消息发送速度更快。
第二个方框概述大概意思就是: 如果运行时确实支持所需的入口点,则此方法将生成调用并返回结果。否则它将返回None并会生成 msgSend 替代。
2)为什么 objc_alloc 只会走一次?
这个问题其实在上个问题的第二个方框的里的注释就有提到,当 tryGenerateSpecializedMessageSend 返回None 的时候就会被 msgSend 替代了。也贴出一下判断的地方
这部分太沉重了,不知道写的是什么就记结果吧,有兴趣的自己亲自去探索探索。
二、isa关联对象与类 从此处我们开始分析 isa 关联对象与类,那就从初始化 isa 源码开始分析
inline void
objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor)
{
assert(!isTaggedPointer());
if (!nonpointer) {
isa.cls = 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;
}
}
源码中有一处有个 shiftcls ,这个变量在 ISA_BITFIELD 结构体,这个结构体在上一篇中有解释,代表类的结构 ,根据newisa.shiftcls = (uintptr_t)cls >> 3 ,这句代码很明显标明了通过 (uintptr_t)cls >> 3 就可以得到类的结构,我们按照这个流程来复现一下看是怎么通过对象找到类。
验证方法一
# 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)
验证方法二
通过
LGTeacher *object = [LGTeacher alloc];
object_getClass(object);
通过这句代码找到方法,查看这个函数的源码流程:object_getClass -> obj->getIsa -> ISA 最后在这里面有句
return (Class)(isa.bits & ISA_MASK);
# define ISA_MASK 0x00007ffffffffff8ULL
通过 & 上一个 ISA_MASK 可以直接拿到这个类的结构了,实际操作一下