接著上篇文章(系統(tǒng)底層源碼分析(18)——objc_msgSend)繼續(xù)說(shuō):
IMP lookUpImpOrForward(Class cls, SEL sel, id inst,
bool initialize, bool cache, bool resolver)
{
...
if (!cls->isRealized()) {
realizeClass(cls);//準(zhǔn)備-父類(lèi)
}
...
retry:
runtimeLock.assertLocked();
// Try this class's cache.
imp = cache_getImp(cls, sel);//從緩存獲取imp
if (imp) goto done;//找到返回
//緩存中沒(méi)有找到就去查找方法列表
// 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;
}
}
//子類(lèi)找不到就遞歸找父類(lèi)
// 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);//從父類(lèi)緩存獲取imp
if (imp) {
if (imp != (IMP)_objc_msgForward_impcache) {
log_and_fill_cache(cls, imp, sel, inst, curClass);
goto done;
}
else {
break;
}
}
//緩存中沒(méi)有找到就去查找父類(lèi)方法列表
// 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);//對(duì)找不到的方法進(jìn)行處理
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;//報(bào)錯(cuò)
cache_fill(cls, sel, imp, inst);
done:
runtimeLock.unlock();
return imp;
}
-
報(bào)錯(cuò)
- 之前說(shuō)到調(diào)用方法昔搂,底層會(huì)調(diào)用
objc_msgSend
,進(jìn)入快速查找流程,找不到就進(jìn)入慢速查找流程荐虐,然后來(lái)到lookUpImpOrForward
泥彤,如果最后還找不到颈抚,就會(huì)調(diào)用_objc_msgForward_impcache
報(bào)錯(cuò):
STATIC_ENTRY __objc_msgForward_impcache
// Method cache version
// THIS IS NOT A CALLABLE C FUNCTION
// Out-of-band Z is 0 (EQ) for normal, 1 (NE) for stret
beq __objc_msgForward
b __objc_msgForward_stret
END_ENTRY __objc_msgForward_impcache
ENTRY __objc_msgForward
// Non-stret version
MI_GET_EXTERN(r12, __objc_forward_handler)
ldr r12, [r12]
bx r12
END_ENTRY __objc_msgForward
上述是匯編代碼,調(diào)用流程是:
__objc_msgForward_impcache
-> __objc_msgForward
-> __objc_forward_handler
-
__objc_forward_handler
會(huì)回到源碼中责静,查找時(shí)需要去掉一個(gè)下劃線(xiàn):
__attribute__((noreturn)) void
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);//打印的錯(cuò)誤信息
}
void *_objc_forward_handler = (void*)objc_defaultForwardHandler;
打印信息是不是很熟悉,就是我們調(diào)用沒(méi)有實(shí)現(xiàn)的方法時(shí)報(bào)的錯(cuò):
-
動(dòng)態(tài)方法決議
- 如果調(diào)用沒(méi)有實(shí)現(xiàn)的方法就會(huì)報(bào)錯(cuò)盖桥,但是系統(tǒng)給了挽救的機(jī)會(huì)灾螃,就在報(bào)錯(cuò)前進(jìn)行處理的
_class_resolveMethod
函數(shù):
void _class_resolveMethod(Class cls, SEL sel, id inst)
{
if (! cls->isMetaClass()) {
// try [cls resolveInstanceMethod:sel]
_class_resolveInstanceMethod(cls, sel, inst);//實(shí)例方法決議
}
else {
// try [nonMetaClass resolveClassMethod:sel]
// and [cls resolveInstanceMethod:sel]
_class_resolveClassMethod(cls, sel, inst);//類(lèi)方法決議
if (!lookUpImpOrNil(cls, sel, inst,
NO/*initialize*/, YES/*cache*/, NO/*resolver*/))
{//根據(jù)isa走位圖,查找實(shí)例方法和類(lèi)方法最終都走向NSObject類(lèi)揩徊,所以再進(jìn)行一次實(shí)例方法決議
_class_resolveInstanceMethod(cls, sel, inst);//實(shí)例方法決議
}
}
}
- 我們先來(lái)看對(duì)實(shí)例方法的處理腰鬼,首先會(huì)查找
resolveInstanceMethod
:
static void _class_resolveInstanceMethod(Class cls, SEL sel, id inst)
{
if (! lookUpImpOrNil(cls->ISA(), SEL_resolveInstanceMethod, cls,
NO/*initialize*/, YES/*cache*/, NO/*resolver*/)) //查找resolveInstanceMethod并緩存
{
// Resolver not implemented.
return;
}
BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;//消息發(fā)送,快速查找
bool resolved = msg(cls, SEL_resolveInstanceMethod, sel);
// Cache the result (good or bad) so the resolver doesn't fire next time.
// +resolveInstanceMethod adds to self a.k.a. cls
IMP imp = lookUpImpOrNil(cls, sel, inst,
NO/*initialize*/, YES/*cache*/, NO/*resolver*/);//再找一次
...
}
IMP lookUpImpOrNil(Class cls, SEL sel, id inst,
bool initialize, bool cache, bool resolver)
{
IMP imp = lookUpImpOrForward(cls, sel, inst, initialize, cache, resolver);//查找
if (imp == _objc_msgForward_impcache) return nil;
else return imp;
}
- 找到之后因?yàn)闀?huì)緩存塑荒,所以下次查找時(shí)熄赡,可以通過(guò)
objc_msgSend
更快查找并調(diào)用,這里要注意找的是系統(tǒng)方法resolveInstanceMethod
:
+ (BOOL)resolveInstanceMethod:(SEL)sel {
return NO;
}
- 這時(shí)可以通過(guò)在
Person
類(lèi)中重寫(xiě)resolveInstanceMethod
來(lái)進(jìn)行處理(動(dòng)態(tài)添加方法):
//Person類(lèi)中
+ (BOOL)resolveInstanceMethod:(SEL)sel{
if (sel == @selector(say)) {
IMP sayHelloIMP = class_getMethodImplementation(self, @selector(sayHello));
Method sayHelloMethod = class_getInstanceMethod(self, @selector(sayHello));
const char *sayHelloType = method_getTypeEncoding(sayHelloMethod);
return class_addMethod(self, sel, sayHelloIMP, sayHelloType);
}
return [super resolveInstanceMethod:sel];
}
返回后齿税,回到第2步彼硫,這時(shí)再次
lookUpImpOrNil
,由于動(dòng)態(tài)添加了方法凌箕,所以這次可以找到方法拧篮,然后返回后就回到第1步進(jìn)行retry
,重新查找并找到牵舱,所以就不會(huì)報(bào)錯(cuò)他托。另外類(lèi)方法處理也是差不多:
/***********************************************************************
* _class_resolveClassMethod
* Call +resolveClassMethod, looking for a method to be added to class cls.
* cls should be a metaclass.
* Does not check if the method already exists.
**********************************************************************/
static void _class_resolveClassMethod(Class cls, SEL sel, id inst)
{
assert(cls->isMetaClass());
if (! lookUpImpOrNil(cls, SEL_resolveClassMethod, inst,
NO/*initialize*/, YES/*cache*/, NO/*resolver*/)) //查找resolveClassMethod并緩存
{
// Resolver not implemented.
return;
}
BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;//消息發(fā)送,快速查找
bool resolved = msg(_class_getNonMetaClass(cls, inst),
SEL_resolveClassMethod, sel);
// Cache the result (good or bad) so the resolver doesn't fire next time.
// +resolveClassMethod adds to self->ISA() a.k.a. cls
IMP imp = lookUpImpOrNil(cls, sel, inst,
NO/*initialize*/, YES/*cache*/, NO/*resolver*/);
...
}
//Person類(lèi)中
+ (BOOL)resolveClassMethod:(SEL)sel{
if (sel == @selector(cls_say)) {
IMP sayHelloIMP = class_getMethodImplementation(objc_getMetaClass("Person"), @selector(cls_sayHello));
Method sayHelloMethod = class_getInstanceMethod(objc_getMetaClass("Person"), @selector(cls_sayHello));
const char *sayHelloType = method_getTypeEncoding(sayHelloMethod);
return class_addMethod(objc_getMetaClass("Person"), sel, sayHelloIMP, sayHelloType);
}
return [super resolveClassMethod:sel];
}
-
消息轉(zhuǎn)發(fā)
如果動(dòng)態(tài)方法決議也沒(méi)有處理仆葡,就會(huì)進(jìn)入消息轉(zhuǎn)發(fā)流程(簡(jiǎn)單來(lái)說(shuō)就是交給別人處理):
- 首先會(huì)進(jìn)入快速轉(zhuǎn)發(fā):
//Person類(lèi)中
- (id)forwardingTargetForSelector:(SEL)aSelector{
if (aSelector == @selector(say)) {
return [Teacher alloc];//返回對(duì)象
}
return [super forwardingTargetForSelector:aSelector];
}
- 如果快速轉(zhuǎn)發(fā)也沒(méi)有處理赏参,就進(jìn)入慢速轉(zhuǎn)發(fā)流程:
2-1. methodSignatureForSelector
返回SEL
方法的簽名,返回的簽名是根據(jù)方法的參數(shù)來(lái)封裝的把篓。這個(gè)函數(shù)讓重載方有機(jī)會(huì)拋出一個(gè)函數(shù)的簽名,用于生成NSInvocation
腰涧,再由后面的 forwardInvocation
去執(zhí)行:
//Person類(lèi)中
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
if (aSelector == @selector(say)) {
return [NSMethodSignature signatureWithObjCTypes:"v@:"];//方法簽名
}
return [super methodSignatureForSelector:aSelector];
}
2-2. 在forwardInvocation
里可以將NSInvocation
多次轉(zhuǎn)發(fā)到多個(gè)對(duì)象中韧掩,這也是這種方式靈活的地方。(而forwarding TargetForSelectorSelec
只能以Selector
的形式轉(zhuǎn)向一個(gè)對(duì)象):
//Person類(lèi)中
- (void)forwardInvocation:(NSInvocation *)anInvocation{
SEL aSelector = [anInvocation selector];
Teacher *teacher = [Teacher alloc];
if ([teacher respondsToSelector:aSelector])
[anInvocation invokeWithTarget:teacher];
else
[super forwardInvocation:anInvocation];
}
- 實(shí)現(xiàn)
forwardInvocation
后窖铡,就算不處理應(yīng)用也不會(huì)再崩潰疗锐。
-
補(bǔ)充
- 為了更充分說(shuō)明這些流程的有效性坊谁,我們可以通過(guò)
instrumentObjcMessageSends
獲取系統(tǒng)日志:
extern void instrumentObjcMessageSends(BOOL flag);//系統(tǒng)方法
int main(int argc, const char * argv[]) {
@autoreleasepool {
Student *student = [Student alloc] ;
instrumentObjcMessageSends(true);//路徑在/tmp/
[student say];
instrumentObjcMessageSends(false);
}
return 0;
}
- 然后直接前往
/tmp
(文件夾路徑),然后打開(kāi)msgSends-
前綴的文件:
//動(dòng)態(tài)方法決議
+ Student NSObject resolveInstanceMethod:
+ Student NSObject resolveInstanceMethod:
//消息轉(zhuǎn)發(fā)-快速
- Student NSObject forwardingTargetForSelector:
- Student NSObject forwardingTargetForSelector:
//消息轉(zhuǎn)發(fā)-慢速
- Student Student methodSignatureForSelector:
- Student Student methodSignatureForSelector:
...
//還會(huì)調(diào)用一次resolveInstanceMethod滑臊,因?yàn)閙ethodSignatureForSelector返回的簽名需要查找方法來(lái)匹配口芍,所以進(jìn)行l(wèi)ookUpImpOrForward查詢(xún)導(dǎo)致又調(diào)用了resolveInstanceMethod
+ Student NSObject resolveInstanceMethod:
+ Student NSObject resolveInstanceMethod:
//
- Student Student forwardInvocation:
+ NSInvocation NSObject initialize
+ NSInvocation NSInvocation _invocationWithMethodSignature:frame:
- NSMethodSignature NSMethodSignature frameLength
...