iOS 底层探索 文章汇总
主要内容:
一、分析慢速查找调用的方法
二、慢速查找源码及流程
三、慢速查找中部分具体实现
分析慢速查找调用的方法
上一章objc_msgSend 流程—快速查找文中分析,如果快速查找,没有找到时,会中转到CheckMiss
/JumpMiss
中标记为NORMAL,并跳转__objc_msgSend_uncached
,本章继续分析
-
__objc_msgSend_uncached
汇编
STATIC_ENTRY __objc_msgSend_uncached
UNWIND __objc_msgSend_uncached, FrameWithNoSaves
MethodTableLookup
TailCallFunctionPointer x17
END_ENTRY __objc_msgSend_uncached
核心是 MethodTableLookup
即查询方法列表
-
MethodTableLookup
汇编实现
.macro MethodTableLookup
// push frame
SignLR
stp fp, lr, [sp, #-16]!
mov fp, sp
......省略......
// lookUpImpOrForward(obj, sel, cls, LOOKUP_INITIALIZE | LOOKUP_RESOLVER)
// receiver and selector already in x0 and x1
mov x2, x16
mov x3, #3
bl _lookUpImpOrForward
// IMP in x0
mov x17, x0
......省略......
.endmacro
核心是 _lookUpImpOrForward
验证:通过汇编调试
验证
-
- 在
main
函数中 对某一对象方法
调用加断点,运行到断点处,开启汇编调试Debug -> Debug worlflow -> Always show Disassembly
,执行进入汇编
- 在
-
2.在
objc_msgSend
行加断点,执行到断点处,按住control + stepinto
, 进入objc_msgSend
汇编
-
3.在
_objc_msgSend_uncached
行加断点,执行到断点处,按住control + stepinto
, 进入_objc_msgSend_uncached
汇编,会发现最后执行到lookUpImpOrForward
注
c/c++
调用汇编
方法,查找该汇编
方法时,要在方法前加 一个下划线
汇编
方法 调用c/c++
方法,查找该方法时,要在方法前减掉一个下划线
二、慢速查找源码及流程
- 全局搜索
lookUpImpOrForward
,在objc-runtime-new.mm
文件中,源码如下:
IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior)
{
// 定义的消息转发
const IMP forward_imp = (IMP)_objc_msgForward_impcache;
IMP imp = nil;
Class curClass;
runtimeLock.assertUnlocked();
// Optimistic cache lookup
// 找缓存 - 快速查找,如果找到则直接返回 imp
// 目的:防止多线程,调用函数时,刚好缓存进来
if (fastpath(behavior & LOOKUP_CACHE)) {
imp = cache_getImp(cls, sel);
if (imp) goto done_nolock;
}
// 加锁,保证读取的线程安全
runtimeLock.lock();
// TODO: this check is quite costly during process startup.
// 判断是否是一个已知的类,判断当前类是否已经被认可,即已经加载的类
checkIsKnownClass(cls);
//判断类是否实现,如果没有,需要先实现,此时的目的是为了确定父类链,方便后续的循环
if (slowpath(!cls->isRealized())) {
cls = realizeClassMaybeSwiftAndLeaveLocked(cls, runtimeLock);
// runtimeLock may have been dropped but is now locked again
}
// 判断类是否初始化,如果没有,需要先初始化
if (slowpath((behavior & LOOKUP_INITIALIZE) && !cls->isInitialized())) {
cls = initializeAndLeaveLocked(cls, inst, runtimeLock);
// runtimeLock may have been dropped but is now locked again
// If sel == initialize, class_initialize will send +initialize and
// then the messenger will send +initialize again after this
// procedure finishes. Of course, if this is not being called
// from the messenger then it won't happen. 2778172
}
runtimeLock.assertLocked();
curClass = cls;
// 查找类的缓存
// unreasonableClassCount - 表示类的迭代的上限
// 死循环 (不存在递归)
for (unsigned attempts = unreasonableClassCount();;) {
// curClass method list.
// 当前类方法列表(采用二分法查找),找到则返回,将方法缓存到cache中
Method meth = getMethodNoSuper_nolock(curClass, sel);
if (meth) {
imp = meth->imp;
goto done;
}
//curClas = 当前类的父类,并判断父类是否为nil
if (slowpath((curClass = curClass->superclass) == nil)) {
// No implementation found, and method resolver didn't help.
// Use forwarding.
// 未找到方法实现,方法解析器也不行,使用转发
imp = forward_imp;
break;
}
// Halt if there is a cycle in the superclass chain.
// 如果父类链中存在循环,则停止
if (slowpath(--attempts == 0)) {
_objc_fatal("Memory corruption in class list.");
}
// Superclass cache.
// 父类缓存
imp = cache_getImp(curClass, sel);
if (slowpath(imp == forward_imp)) {
// Found a forward:: entry in a superclass.
// Stop searching, but don't cache yet; call method
// resolver for this class first.
// 如果在父类中找到imp 的值是forward_imp,则停止查找,跳出循环
break;
}
if (fastpath(imp)) {
// Found the method in a superclass. Cache it in this class.
// 如果在父类中,找到了方法,将其存储到cache中
goto done;
}
}
// No implementation found. Try method resolver once.
// 没有找到方法实现,尝试一次方法解析
if (slowpath(behavior & LOOKUP_RESOLVER)) {
// 动态方法决议的控制条件,表示流程只走一次
behavior ^= LOOKUP_RESOLVER;
return resolveMethod_locked(inst, sel, cls, behavior);
}
done:
// 存储到缓存
log_and_fill_cache(cls, imp, sel, inst, curClass);
// 解锁
runtimeLock.unlock();
done_nolock:
if (slowpath((behavior & LOOKUP_NIL) && imp == forward_imp)) {
return nil;
}
return imp;
}
流程图
总结主要步骤
- 在
cache
缓存中进行查找(快速查找
),找到就返回
,没有找到向下执行
- 判断
cls
,是否是已知类
否 -> 则报错
,类是否实现
否->实现并确定父类链
,是否初始化
否 ->初始化
,- for循环(是死循环),按照
类继承链 或 元类继承链
的顺序查找
- 先在cls的
方法列表
中使用二分法查找
,找到: 则进入cache写入流程
并返回imp
。未找到:cls = cls->superclass
- 现在 cls 是父类,先
判空
,在cls 的缓存中查找
,找到: 则进入cache写入流程
并返回imp
,未找到:回到第三步for 循环
4.判断是否执行过
动态方法解析
否 -> 执行动态方法解析(只执行一次
)
三、慢速查找中部分具体实现
下面分析 二分查找原理
、 父类缓存查找
步骤 及方法未实现时报错源码
-
getMethodNoSuper_nolock
使用二分法查找
方法查找流程:
getMethodNoSuper_nolock
->search_method_list_inline
->findMethodInSortedMethodList
findMethodInSortedMethodList
源码
ALWAYS_INLINE static method_t *
findMethodInSortedMethodList(SEL key, const method_list_t *list)
{
ASSERT(list);
const method_t * const first = &list->first;
const method_t *base = first;
const method_t *probe;
// 当前查找的方法
uintptr_t keyValue = (uintptr_t)key;
uint32_t count;
// base是第一个,count是最后一个,probe是中间。 二分查找
// count>>=1 -> count = count >> 1
for (count = list->count; count != 0; count >>= 1) {
// 从首地址+下标 -> 移动到中间位置 count>>1 == count/2
probe = base + (count >> 1);
uintptr_t probeValue = (uintptr_t)probe->name;
// 如果找到,返回中间位置
if (keyValue == probeValue) {
// while 平移 -- 排除分类重名方法
while (probe > first && keyValue == (uintptr_t)probe[-1].name) {
// 分类方法在前面,优先调用分类的方法
probe--;
}
return (method_t *)probe;
}
// 如果 keyValue 大于 probeValue, 就往probe即中间位置的右边查找
if (keyValue > probeValue) {
base = probe + 1;
count--;
}
}
return nil;
}
执行流程
- 汇编
_cache_getImp
父类缓存查找,核心代码CacheLookup
,CacheLookup
在上一章objc_msgSend 流程—快速查找 有介绍
STATIC_ENTRY _cache_getImp
GetClassFromIsa_p16 p0
// 缓存查找
CacheLookup GETIMP, _cache_getImp
LGetImpMiss:
mov p0, #0
ret
END_ENTRY _cache_getImp
父类缓存查找流程:
- 方法未实现时报错
如果在快速查找、慢速查找、方法解析流程中,都没有找到实现,则使用消息转发,其流程如下:
__objc_msgForward_impcache
->__objc_msgForward
->__objc_msgForward
->__objc_forward_handler
(c/c++实现_objc_forward_handler
) ->objc_defaultForwardHandler
消息转发源码:
-
__objc_msgForward_impcache
汇编源码,跳转到__objc_msgForward
STATIC_ENTRY __objc_msgForward_impcache
// No stret specialization.
b __objc_msgForward
END_ENTRY __objc_msgForward_impcache
-
__objc_msgForward
汇编源码,核心是__objc_forward_handler
ENTRY __objc_msgForward
adrp x17, __objc_forward_handler@PAGE
ldr p17, [x17, __objc_forward_handler@PAGEOFF]
TailCallFunctionPointer x17
END_ENTRY __objc_msgForward
-
__objc_forward_handler
对应的c++
代码_objc_forward_handler
,本质是objc_defaultForwardHandler
方法
void *_objc_forward_handler = (void*)objc_defaultForwardHandler;
-
objc_defaultForwardHandler
源码,函数未实现
时报的错
// Default forward handler halts the process.
__attribute__((noreturn, cold)) void
objc_defaultForwardHandler(id self, SEL sel)
{
_objc_fatal("%c[%s %s]: unrecognized selector sent to instance %p "
"(no message forward handler is installed)",
class_isMetaClass(object_getClass(self)) ? '+' : '-',
object_getClassName(self), sel_getName(sel), self);
}