一、什么是Runtime?
OC是一門動(dòng)態(tài)類型的語言,它允許很多操作都推遲到程序運(yùn)行時(shí)再進(jìn)行
OC的動(dòng)態(tài)性就是由Runtime來支撐和實(shí)現(xiàn)的,而Runtime是一套C語言的API,它封裝了很多動(dòng)態(tài)性相關(guān)的函數(shù)
我們平時(shí)編寫的OC代碼,其實(shí)底層都是將代碼轉(zhuǎn)換成了Runtime API來進(jìn)行調(diào)用
二、OC的消息機(jī)制
OC的方法調(diào)用其實(shí)都是轉(zhuǎn)成了objc_msgSend函數(shù)的調(diào)用,給方法的調(diào)用者發(fā)送了一條消息。
objc_msgSend底層有3個(gè)階段
- 消息發(fā)送(當(dāng)前類、父類中查找)
- 動(dòng)態(tài)方法解析
- 消息轉(zhuǎn)發(fā)
三、流程解析
例如,我們有一個(gè)Person類,將這個(gè)Person實(shí)例化對(duì)象
先導(dǎo)入頭文件
#import <objc/runtime.h>
#import <objc/message.h>
#import "Person.h"
//oc寫法
Person *person1 = [[Person alloc] init];
//runtime寫法
Person *person2 = objc_msgSend(objc_msgSend([Person class], @selector(alloc)), @selector(init));
新工程這里會(huì)報(bào)錯(cuò)
Too many arguments to function cal
這個(gè)問題只需要在FuDemo->Target中Build Setting的Enable Strict Checking of objc_msgSend Calls的值設(shè)置為NO即可。
運(yùn)行程序,我們能看到,person1與person2都創(chuàng)建成功了,接下來就看看匯編代碼片段是不是執(zhí)行了objc_msgSend方法。首先將Person1的初始化代碼注釋掉,然后打開Always Show Disassembly,讓我們?cè)谡{(diào)試時(shí),斷點(diǎn)能直接進(jìn)入到匯編代碼界面,如下圖:

從匯編代碼界面,我們能看到如下信息:
Person進(jìn)行了alloc,然后該對(duì)象調(diào)用了
objc_msgSend進(jìn)行init。將斷點(diǎn)執(zhí)行到調(diào)用objc_msgSend方法,‘按住Control + step into’查看
objc_msgSend內(nèi)部實(shí)現(xiàn),再用同樣方法查看objc_msgSend_uncached,最終我們可以找到class_lookupMethodAndLoadCache3這樣的調(diào)用步驟。
四、objc_msgSend執(zhí)行流程
工程搜索objc_msgSend,找到objc-msg-x86_64.s我們可以找到如下代碼片段
/********************************************************************
*
* id objc_msgSend(id self, SEL _cmd,...);
* IMP objc_msgLookup(id self, SEL _cmd, ...);
*
* objc_msgLookup ABI:
* IMP returned in r11
* Forwarding returned in Z flag
* r10 reserved for our use but not used
*
********************************************************************/
這下面就是實(shí)現(xiàn)的主要步驟,我們抽取主要信息查看
/*進(jìn)入到objc_msgSend*/
ENTRY _objc_msgSend
/*查找當(dāng)前isa*/
GetIsaFast NORMAL // r10 = self->isa
/*從緩存中查找當(dāng)前方法,如果查找到了IMP將結(jié)果返回給調(diào)用者*/
CacheLookup NORMAL, CALL // calls IMP on success
/*在緩存中沒找到,搜索方法列表*/
// cache miss: go search the method lists
LCacheMiss:
/*\*/
// isa still in r10
/*跳轉(zhuǎn)objc_msgSend_uncached*/
jmp __objc_msgSend_uncached
objc_msgSend_uncached內(nèi)基本都是在調(diào)用MethodTableLookup(從當(dāng)前方法列表中查找)。如果找到,則將IMP放到寄存器中。
MethodTableLookup能找到class_lookupMethodAndLoadCache3被調(diào)用,正好這也是之前我們所驗(yàn)證的最后一部。
在class_lookupMethodAndLoadCache3中只做了lookUpImpOrForward(查找方法實(shí)現(xiàn))這一件事
因此runtime的執(zhí)行順序?yàn)椋?/p>
1.objc_msgSend
2.CacheLookup(有緩存IMP,則返回給調(diào)用者。沒有緩存則往下執(zhí)行)
3.objc_msgSend_unCached
4.MethodTableLookup
5.class_lookupMethodAndLoadCache3
6.lookUpImpOrForward
五、lookUpImpOrForward代碼片段注釋
IMP lookUpImpOrForward(Class cls, SEL sel, id inst,
bool initialize, bool cache, bool resolver)
{
/*從緩存中查找*/
if (cache) {
imp = cache_getImp(cls, sel);
if (imp) return imp;
}
/*確保當(dāng)前isa初始化*/
checkIsKnownClass(cls);
if (!cls->isRealized()) {
realizeClass(cls);
}
if (initialize && !cls->isInitialized()) {
runtimeLock.unlock();
_class_initialize (_class_getNonMetaClass(cls, inst));
runtimeLock.lock();
}
retry:
runtimeLock.assertLocked();
/*從當(dāng)前類的緩存中查找*/
imp = cache_getImp(cls, sel);
if (imp) goto done;
/*從當(dāng)前類的方法列表中查找*/
{
Method meth = getMethodNoSuper_nolock(cls, sel);
if (meth) {
/*將查找到的meth放入緩存中*/
log_and_fill_cache(cls, meth->imp, sel, inst, cls);
imp = meth->imp;
goto done;
}
}
/*沿著繼承鏈向上查找*/
{
unsigned attempts = unreasonableClassCount();
for (Class curClass = cls->superclass;
curClass != nil;
curClass = curClass->superclass)
{
if (--attempts == 0) {
_objc_fatal("Memory corruption in class list.");
}
imp = cache_getImp(curClass, sel);
if (imp) {
if (imp != (IMP)_objc_msgForward_impcache) {
log_and_fill_cache(cls, imp, sel, inst, curClass);
goto done;
}
else {
break;
}
}
Method meth = getMethodNoSuper_nolock(curClass, sel);
if (meth) {
log_and_fill_cache(cls, meth->imp, sel, inst, curClass);
imp = meth->imp;
goto done;
}
}
}
/*沒有找到IMP,嘗試動(dòng)態(tài)決議 resolveInstanceMethod*/
if (resolver && !triedResolver) {
runtimeLock.unlock();
_class_resolveMethod(cls, sel, inst);
runtimeLock.lock();
triedResolver = YES;
goto retry;
}
/*沒有實(shí)現(xiàn)動(dòng)態(tài)決議方法,觸發(fā)消息轉(zhuǎn)發(fā)流程 (forwardingTargetForSelector和forwardInvocation)*/
imp = (IMP)_objc_msgForward_impcache;
cache_fill(cls, sel, imp, inst);
done:
runtimeLock.unlock();
return imp;
}