- 在對象調(diào)用方法是Objective-C中經(jīng)常使用的功能,也就是消息的傳遞,而Objective-C是C的超集,所以和C不同的是,Objective-C使用的是動(dòng)態(tài)綁定,也就是runtime。
方法調(diào)用流程
1、編譯器會(huì)把 [self doSomething] 轉(zhuǎn)化objc_msgSend(ViewController,SEL),SEL為@selector(doSomething)。
2、Runtime會(huì)在self對象所對應(yīng)的ViewController類的方法緩存列表里查找方法的SEL(cache)
3、如果沒有找到,則在ViewController類的方法分發(fā)表查找方法的SEL。(類由對象isa指針指向,方法分發(fā)表即method_list)
4、如果沒有找到,則在其父類的方法分發(fā)表里查找方法的SEL
(父類由類的superClass指向)
5、如果沒有找到,則沿繼承體系繼續(xù)下去,最終到達(dá)NSObject類。
6、如果在2345的其中一步中找到,則定位了方法實(shí)現(xiàn)的入口,執(zhí)行具體實(shí)現(xiàn)
7、如果還是沒找到那就會(huì)面臨兩種情況:
① 如果是使用[self doSomething]的方式調(diào)用方法 ② 使用[self performSelector:@selector(doSomething)]的方式調(diào)用方法
對與①情況編譯器會(huì)直接報(bào)錯(cuò),而對于②情況需要到運(yùn)行時(shí)才能確定對象能否接收指定的消息,這時(shí)候會(huì)進(jìn)入下面所說的消息轉(zhuǎn)發(fā)的流程;
消息轉(zhuǎn)發(fā)
1.IMP是”implementation”的縮寫,它是objetive-C 方法(method)實(shí)現(xiàn)代碼塊的地址,可像C函數(shù)一樣直接調(diào)用。通常情況下我們是通過[object method:parameter]或objc_msgSend()的方式向?qū)ο蟀l(fā)送消息,然后Objective-C運(yùn)行時(shí)尋找匹配此消息的IMP,然后調(diào)用它
2.Objetive-C中的Method結(jié)構(gòu)
在Objecitve-C中,在類中對每一個(gè)方法有一個(gè)在運(yùn)行時(shí)構(gòu)建的數(shù)據(jù)結(jié)構(gòu),在Objective-C 2.0中,此結(jié)構(gòu)對用戶不可見,但仍在內(nèi)部存在。
struct objc_method { SEL method_name ;//方法名為此方法的簽名 char *method_types ;//方法類型描述了參數(shù)的類型。 IMP method_imp ;//函數(shù)指針,為方法具體實(shí)現(xiàn)代碼塊的地址 }

- 階段一
+ (BOOL)resolveInstanceMethod:(SEL)sel { if (sel == @selector(doSomething)) { NSLog(@"add method here"); class_addMethod([self class],sel, (IMP)dynamicMethodIMP,"v@:"); return YES; } return [super resolveInstanceMethod:sel]; }
- 階段二
這時(shí)候已經(jīng)默許了你并不想使用消息接收者來響應(yīng)這個(gè)方法,所以我們需要找到一個(gè)接盤俠
- (id)forwardingTargetForSelector:(SEL)aSelector { Class class=NSClassFromString(@"BBViewController"); UIViewController *vc = class.new; if (aSelector == NSSelectorFromString(@"secondVCMethod")){ NSLog(@"secondVC do this !"); return vc; } return nil; }
- 階段三
runtime需要生成一個(gè)methodSignature變量來組裝,這將通過調(diào)用消息接收者的-(NSMethodSignature *)methodSignatureForSelector:
獲取,這個(gè)變量包含了方法的參數(shù)類型、參數(shù)個(gè)數(shù)以及消息接收者等信息。接著把這個(gè)變量組裝成一個(gè)NSInvocation對象進(jìn)行最后一次的消息轉(zhuǎn)發(fā),調(diào)用接收者的-forwardInvocation: 來進(jìn)行最后的挽救機(jī)會(huì)
(void)forwardInvocation:(NSInvocation *)anInvocation { Class class=NSClassFromString(@"BBViewController"); UIViewController *vc = class.new; if ([class instancesRespondToSelector:anInvocation.selector]) { [anInvocation invokeWithTarget:vc]; } }
參考文章
http://www.cocoawithlove.com/2008/02/imp-of-current-method.html
延伸
https://github.com/bang590/JSPatch/blob/master/README-CN.md