熟悉OC语言的Runtime(运行时)机制以及对象方法调用机制的开发者都知道,全部OC方法调用在编译时都会转化为对C函数objc_msgSend的调用。缓存
objc_msgSend函数是全部OC方法调用的核心引擎,它负责查找真实的类或者对象方法的实现,并去执行这些方法函数。因调用频率是如此之高,因此要求其内部实现近可能达到最高的性能。这个函数的内部代码实现是用汇编语言来编写的。你能够在runtime源码下查看各类体系架构下的汇编语言的实现。架构
咱们选择经常使用的arm64来查看:app
能够看到,先经过LNilOrTargged判断对象是不是targetpoint类型或nil,而后对isa进行一系列的寄存器操做,最终获得LGetIsaDone(isa处理完毕),isa处理完毕后,开始执行CacheLookup NORMAL (在缓存cache_t里寻找imp,找到的话返回imp,未找到的话返回objc_msgSend_uncached),下面咱们跳入CacheLookup的实现函数
能够看到有三种状况:CacheHit、CheckMiss、add。性能
先来查看CacheHit:this
由于咱们传过来的是NORMAL,因此紧接着执行TailCallCachedImp,回调imp。3d
再来看CheckMiss:cdn
一样,传入的是NORMAL,因此会回调objc_msgSend_uncached,接着跳入objc_msgSend_uncached:对象
再跳入MethodTableLookup(能够猜到是去经过MethodTable方法列表去查找,事实也确实是这样的,结构体objc_class里有一个class_rw_t中存储着方法列表method_array_t (是一张哈希表,对method_t的name和imp进行一一对应),属性列表property_array_t,协议列表protocol_array_t 等内容),而后去回调FunctionPointer。再来查看MethodTableLookup:blog
也是咔咔一顿看不懂的汇编操做后到了class_lookupMethodAndLoadCache3,再跳入具体实现:
能够看到,该函数是经过c来实现的(终于告别了难缠的汇编):咱们继续跳:
IMPlookUpImpOrForward(Classcls,SELsel,idinst,
boolinitialize,boolcache,boolresolver)
{
IMPimp =nil;
booltriedResolver =NO;
runtimeLock.assertUnlocked();
// Optimistic cache lookup
if(cache) {
// 若是cache是YES,则从缓存中查找IMP
imp =cache_getImp(cls, sel);
if(imp)returnimp;
}
runtimeLock.lock();
checkIsKnownClass(cls);
if(!cls->isRealized()) { 判断类是否已经被建立,若是没有被建立,则将类实例化
realizeClass(cls);
}
if(initialize && !cls->isInitialized()) { 第一次调用当前类的话,执行initialize的代码
runtimeLock.unlock();
_class_initialize (_class_getNonMetaClass(cls, inst));
runtimeLock.lock();
// 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
}
retry:
runtimeLock.assertLocked();
// Try this class's cache. 再次尝试获取这个类的缓存
imp =cache_getImp(cls, sel);
if(imp)gotodone;
// Try this class's method lists.
{ 若是没有从cache中查找到,则从方法列表中获取Method
Method meth = getMethodNoSuper_nolock(cls, sel);
if(meth) {
log_and_fill_cache(cls, meth->imp, sel, inst, cls);
imp = meth->imp;
gotodone;
}
}
// Try superclass caches and method lists.
{ 若是尚未,就从父类缓存或者方法列表获取imp
unsignedattempts =unreasonableClassCount();
for(ClasscurClass = cls->superclass;
curClass !=nil;
curClass = curClass->superclass)
{
// Halt if there is a cycle in the superclass chain.
if(--attempts ==0) {
_objc_fatal("Memory corruption in class list.");
}
// Superclass cache.
imp =cache_getImp(curClass, sel);
if(imp) {
if(imp != (IMP)_objc_msgForward_impcache) {
// Found the method in a superclass. Cache it in this class.
log_and_fill_cache(cls, imp, sel, inst, curClass);
gotodone;
}
else{
// Found a forward:: entry in a superclass.
// Stop searching, but don't cache yet; call method
// resolver for this class first.
break;
}
}
// Superclass method list.
Methodmeth =getMethodNoSuper_nolock(curClass, sel);
if(meth) {
log_and_fill_cache(cls, meth->imp, sel, inst, curClass);
imp = meth->imp;
gotodone;
}
}
}
// 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;
gotoretry;
}
// 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();
returnimp;
}
lookUpImpOrForward 这个方法里面篇幅很长里面介绍了如下几点(如下涉及了消息,动态方法解析,还有消息转发,咱们这篇文章不作描述。) :
若是cache是YES,则从缓存中查找IMP。这里也就是说咱们若是以前响应过的,在cache存过,就不须要下面的操做了
判断类是否已经被建立,若是没有被建立,则将类实例化
第一次调用当前类的话,执行initialize的代码
尝试获取这个类的缓存 (这里不少小伙伴就会质疑,为何还要取一次内存,要知道OC是动态语言,在咱们执行这个获取imp的时候,外界在开锁,解锁的时候是能够访问的,动态操做)
若是没有从cache中查找到,则从方法列表中获取Method
若是尚未,就递归从父类缓存或者方法列表获取imp
若是没有找到,则尝试动态方法解析:_class_resolveMethod
若是没有IMP被发现,而且动态方法解析也没有处理,则进入消息转发阶段:_objc_msgForward_impcache