熟悉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