当我们调用方法时,进入汇编模式可以发现,底层其实会调用
objc_msgSend
进行快速查找,这个方法是用汇编写的,详请我们就不看了,就主要看流程:1.对接受者进行判空处理:检查这个
selector
是不是要忽略。检测这个selector
的target
是不是nil
,OC
允许我们对一个nil
对象执行任何方法不会崩溃,因为运行时会被忽略掉。
2.进行taggedPoint
等异常处理
3.获取到接受者isa
,对isa & mask
获取到class
4.通过对class
的isa
进行指针偏移,获取到cache_t
5.通过对cache_t
中key & mask
获取到下标,查找到对应的bucket
,获取到其中的IMP
6.如果上述快速查找流程没有找到IMP
,就走到__objc_msgSend_uncached
中的MethodTableLookup
开始慢速查找(最终调用_class_lookupMethodAndLoadCache3
)
(详情可以看参考文章)
-
源码
- 从objc4-750源码探究:
IMP _class_lookupMethodAndLoadCache3(id obj, SEL sel, Class cls)
{
return lookUpImpOrForward(cls, sel, obj,
YES/*initialize*/, NO/*cache*/, YES/*resolver*/);
}
IMP lookUpImpOrForward(Class cls, SEL sel, id inst,
bool initialize, bool cache, bool resolver)
{
...
if (!cls->isRealized()) {
realizeClass(cls);//准备-父类
}
...
retry:
runtimeLock.assertLocked();
// Try this class's cache.
imp = cache_getImp(cls, sel);//从缓存获取imp
if (imp) goto done;//找到返回
//缓存中没有找到就去查找方法列表
// Try this class's method lists.
{//局部作用域,可避免名字冲突
Method meth = getMethodNoSuper_nolock(cls, sel);//查找
if (meth) {
log_and_fill_cache(cls, meth->imp, sel, inst, cls);//缓存
imp = meth->imp;
goto done;
}
}
//子类找不到就递归找父类
// Try superclass caches and method lists.
{
unsigned attempts = unreasonableClassCount();
for (Class curClass = cls->superclass;
curClass != nil;
curClass = curClass->superclass)
{
// Halt if there is a cycle in the superclass chain.
if (--attempts == 0) { ... }
// Superclass cache.
imp = cache_getImp(curClass, sel);//从父类缓存获取imp
if (imp) {
if (imp != (IMP)_objc_msgForward_impcache) {
log_and_fill_cache(cls, imp, sel, inst, curClass);
goto done;
}
else {
break;
}
}
//缓存中没有找到就去查找父类方法列表
// Superclass method list.
Method meth = getMethodNoSuper_nolock(curClass, sel);//查找
if (meth) {
log_and_fill_cache(cls, meth->imp, sel, inst, curClass);//缓存
imp = meth->imp;
goto done;
}
}
}
// No implementation found. Try method resolver once.
if (resolver && !triedResolver) {
runtimeLock.unlock();
_class_resolveMethod(cls, sel, inst);//对找不到的方法进行处理
runtimeLock.lock();
// Don't cache the result; we don't hold the lock so it may have
// changed already. Re-do the search from scratch instead.
triedResolver = YES;
goto retry;//再次查找
}
// No implementation found, and method resolver didn't help.
// Use forwarding.
imp = (IMP)_objc_msgForward_impcache;//报错
cache_fill(cls, sel, imp, inst);
done:
runtimeLock.unlock();
return imp;
}
- 如果从类(
objc_class
)的缓存(cache
)中找到方法就返回,没有就从方法列表中查找:
static method_t *
getMethodNoSuper_nolock(Class cls, SEL sel)
{
runtimeLock.assertLocked();
assert(cls->isRealized());
// fixme nil cls?
// fixme nil sel?
for (auto mlists = cls->data()->methods.beginLists(),
end = cls->data()->methods.endLists();
mlists != end;
++mlists)
{
method_t *m = search_method_list(*mlists, sel);
if (m) return m;
}
return nil;
}
static method_t *search_method_list(const method_list_t *mlist, SEL sel)
{
int methodListIsFixedUp = mlist->isFixedUp();
int methodListHasExpectedSize = mlist->entsize() == sizeof(method_t);
if (__builtin_expect(methodListIsFixedUp && methodListHasExpectedSize, 1)) {
return findMethodInSortedMethodList(sel, mlist);//二分法查找方法
} else { ... }
...
return nil;
}
- 找到方法后对其进行缓存:
static void
log_and_fill_cache(Class cls, IMP imp, SEL sel, id receiver, Class implementer)
{
...
cache_fill (cls, sel, imp, receiver);
}
void cache_fill(Class cls, SEL sel, IMP imp, id receiver)
{
#if !DEBUG_TASK_THREADS
mutex_locker_t lock(cacheUpdateLock);
cache_fill_nolock(cls, sel, imp, receiver);
#else
_collecting_in_critical();
return;
#endif
}
最后调用cache_fill_nolock
进行缓存。(详情看上篇:类的cache)
- 如果在当前类没找到,就递归往上找,流程与当前类一样。找到后进行返回(
goto done
)。
- 如果真的找不到,就会进入特殊处理并再次查找(
goto retry
),如果还失败就进行报错。(详情看下篇:动态方法决议&消息转发)
快速查找
objc_msgSend
慢速查找lookUpImpOrForward
-
验证
@interface NSObject (Test)
- (void)obj_say;
+ (void)obj_cls_say;
@end
@interface Person : NSObject
- (void)p_say;
+ (void)p_cls_say;
@end
@interface Student : Person
- (void)s_say;
+ (void)s_cls_say;
@end
//调用
Student *student = [[Student alloc] init];
[student s_say];//Student
[student p_say];//Student->Person
[student obj_say];//Student->Person->NSObject
[Student s_cls_say];//Student元类
[Student p_cls_say];//Student元类->Person元类
[Student obj_cls_say];//Student元类->Person元类->NSObject元类
[Student obj_say];//Student元类->Person元类->NSObject元类(根元类)->NSObject
前面6个方法都执行,很正常,但是最后一个方法也能执行。因为类方法存在于元类中,递归往上查找方法时便找到NSObject元类
的父类,也就是NSObject
,NSObject
该类中保存的是对象方法,便找到了obj_say
。
网友评论