1.OC调用方法的本质
我们都知道或听过,比如 在OC里面 写 [preson sayNB] 那么底层实际走的是objc_msgSend 的API :objc_msgSend(person, @selector(sayNB))
这部分源码在可以在苹果官方开源的objc里面查看。如何你想去了解 可以看看objc-msg-arm64.s ;这是个纯汇编代码;也就是说objc_msgSend是用汇编来实现;为什么要用汇编,因为汇编快!
2.汇编快速查找cache
里面大概主流程如下:
先进入 ENTRY _objc_msgSend
1.cmp p0, #0 // nil check and tagged pointer check 判断p0(消息接收者 就是person)是否存在;存在就往下走
2.GetClassFromIsa_p16 p13, 1, x0 // p16 = class, 这里可以拿到 isa和class
3.CacheLookup NORMAL, _objc_msgSend, __objc_msgSend_uncached
3.1 LLookupStart (CACHE_MASK_STORAGE_HIGH_16 为真机架构流程)
//获取缓存cache
ldr p11, [x16, #CACHE] // p11 = mask|buckets
// p11的位置是否为0 不为0就跑LLookupPreopt
tbnz p11, #0, LLookupPreopt\Function
and p10, p11, #0x0000ffffffffffff // p10 = buckets
// p1 sel>>7 因为cache 存入时是 value ^= value >>7 就是一样的操作
eor p12, p1, p1, LSR #7
//把buckets右移出去 剩下mask 实质是做 (_cmd ^(_cmd>>7)) & mask这样就拿到哈希 index
and p12, p12, p11, LSR #48 // x12 = (_cmd ^ (_cmd >> 7)) & mask
//index << 4 意思 从开始的buckets 开始偏移 到index指向的bucket位置
add p13, p10, p12, LSL #(1+PTRSHIFT)
// p13 = buckets + ((_cmd & mask) << (1+PTRSHIFT))
// do {
1: ldp p17, p9, [x13], #-BUCKET_SIZE // {imp, sel} = *bucket-- 意思是拿到对应的{imp,sel}到p9 比较
cmp p9, p1 // if (sel != _cmd) {
b.ne 3f // scan more
// } else {
2: CacheHit \Mode // hit: call or return imp 对比相同 就是缓存命中 意思是找到了
// }
3: cbz p9, \MissLabelDynamic // if (sel == 0) goto Miss; //sel为0 意味着bucket为空 就是没有了
cmp p13, p10 // } while (bucket >= buckets) //继续循环
b.hs 1b
如果没有CacheHit 怎么办?那就到MissLabelDynamic ;
__objc_msgSend_uncached
MethodTableLookup流程
_lookUpImpOrForward
3.慢速查找methodlist
到了这里 我们终于可以离开晦涩难懂的汇编;回到C++代码了 objc-runtime-new.mm里面
在缓存里面找不到方法, lookUpImpOrForward 慢速查找methodlist。
也就说 在汇编的_objc_msgSend 快速查找缓存里面是有这个方法;没有的到c++的lookUpImpOrForward 里面进行慢速查找methodlist。
那我们看看 底层是如果在 lookUpImpOrForward 里查找imp的?
在for循环里面
for (unsigned attempts = unreasonableClassCount();;){
//这个还是先查一次共享缓存,因为我们在进来lookUpImpOrForward 有对类进行初始化,有有相关函数进入共享缓存,如果调用的刚好是这些方法 那么久可以拿到IMP了
if (curClass->cache.isConstantOptimizedCache(/* strict */true)) {
#if CONFIG_USE_PREOPT_CACHES
imp = cache_getImp(curClass, sel);
if (imp) goto done_unlock;
curClass = curClass->cache.preoptFallbackClass();
#endif
} else {
// curClass method list.
Method meth = getMethodNoSuper_nolock(curClass, sel); //这里进入 search_method_list_inline 再走 findMethodInSortedMethodList (类在加载的时候会按sel的地址排序) 再走 findMethodInSortedMethodList 里面进入了二分查找查询方法
if (meth) {//找到就结束
imp = meth->imp(false);
goto done;
}
if (slowpath((curClass = curClass->getSuperclass()) == nil)) {//找父类 判断父类是否为空
// No implementation found, and method resolver didn't help.
// Use forwarding.
imp = forward_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.
break;
}
if (fastpath(imp)) {
// Found the method in a superclass. Cache it in this class.
goto done;
}
}
4.找不到方法报错
在慢查找还没不到 就会在objc_defaultForwardHandler 报错
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);
}
看上面才知道 原来所谓的+方法 - 方法 只不过是OC自己加上去的符号而已。