消息發(fā)送的正常處理流程
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
Person *p = [Person new];
// 調(diào)用 p 身上一個不存在的方法,就會進(jìn)入消息轉(zhuǎn)發(fā)
SEL sel = NSSelectorFromString(@"caonima");
[p performSelector:sel];
}
- 首先根據(jù)當(dāng)前對象
reciver對象的isa指針獲取它的類型。 - 優(yōu)先在
class的cache找到message方法,如果找不到,就去methodList方法列表里面去找。 - 如果在
classcache&methodList都沒有找到,會去superClass里面去找。 - 一旦找到了
message方法,就執(zhí)行該方法的IMP。

消息的處理和轉(zhuǎn)發(fā)流程(異常處理)
如果按照上述流程,仍然無法找到 message 的實現(xiàn),消息就會進(jìn)入轉(zhuǎn)發(fā)階段。(也就是在報 unrecognized selector sent to instance 0xxxxxx之前的階段)
進(jìn)入“異常處理”的階段,也叫做消息轉(zhuǎn)發(fā)。

如果一個對象以及它的繼承鏈條上都沒有找到這個消息,那么就會進(jìn)入到消息轉(zhuǎn)發(fā)的階段。
消息轉(zhuǎn)發(fā)的第一個階段:+ (BOOL)resolveInstanceMethod:(SEL)sel
當(dāng)前 SEL 會轉(zhuǎn)發(fā)給當(dāng)前調(diào)用此方法的類。確認(rèn),自己是否真的無法完成此條消息的處理。
在這個方法中,返回一個 YES。告知系統(tǒng),我可以處理這個消息。并動態(tài)的為此類添加這個方法。
//SEL 是結(jié)構(gòu)體指針 IMP 是函數(shù)指針變量
void myMethodImp(id self,SEL _cmd) {
NSLog(@"%@",@"我是 person 的方法實現(xiàn)");
}
+ (BOOL)resolveInstanceMethod:(SEL)sel {
NSString *selName = NSStringFromSelector(sel);
if ([selName isEqualToString:@"caonima"]) {
class_addMethod([self class], sel, (IMP)myMethodImp, "v@:");
return YES;// 告訴外界,我可以執(zhí)行這個 sel,你在返回去執(zhí)行一次吧。
}
return NO; // 告訴,我無法執(zhí)行這個 sel。消息轉(zhuǎn)發(fā)進(jìn)入到下一個步驟。
}
運行結(jié)果:
2017-10-14 17:27:52.548 CodeFor消息轉(zhuǎn)發(fā)[81717:21305980] 我是 person 的方法實現(xiàn)
或者直接返回一個 nil,告知系統(tǒng),我無法處理這個消息,你接著去下一個階段吧。
消息轉(zhuǎn)發(fā)就進(jìn)入到第二步:- (id)forwardingTargetForSelector:(SEL)aSelector。
消息轉(zhuǎn)發(fā)第二步:- (id)forwardingTargetForSelector:(SEL)aSelector
如果消息轉(zhuǎn)發(fā)第一步返回 nil,那么就會進(jìn)入到此步。
/**
告訴,系統(tǒng),F(xiàn)ahter可以執(zhí)行這個方法
*/
- (id)forwardingTargetForSelector:(SEL)aSelector {
NSString *strSel = NSStringFromSelector(aSelector);
if ([strSel isEqualToString:@"caonima"]) {
return [Father new];
}
return nil;// 告訴系統(tǒng),我也無法找到一個可以執(zhí)行此方法的對象
}
運行結(jié)果:
2017-10-14 17:38:01.768 CodeFor消息轉(zhuǎn)發(fā)[81937:21345385] 我是 father,我可以執(zhí)行 caonima 這個消息
這一步的意義:作為接收消息的我來說,無法處理這個消息。但是我知道誰可以處理。
當(dāng)然,如果當(dāng)前我也不知道,誰可以處理這個消息,那么直接返回 nil。
消息轉(zhuǎn)發(fā)將會進(jìn)入到第三步驟。
消息轉(zhuǎn)發(fā)的第三步:- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector & - (void)forwardInvocation:(NSInvocation *)anInvocation
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
動態(tài)的拼接一個Objc 方法的簽名。如果返回 nil 或者一個錯誤的方法簽名,將執(zhí)行 doesNotRecognizeSelector
- (void)doesNotRecognizeSelector:(SEL)aSelector {
NSLog(@"%@",@"沒有返回一個正確的方法簽名!");
[super doesNotRecognizeSelector:aSelector];
}
2017-10-14 17:58:07.296 CodeFor消息轉(zhuǎn)發(fā)[82176:21394061] 沒有返回一個正確的方法簽名!
2017-10-14 17:58:07.296 CodeFor消息轉(zhuǎn)發(fā)[82176:21394061] -[Person caonima]: unrecognized selector sent to instance 0x618000009720
- (void)forwardInvocation:(NSInvocation *)anInvocation
動態(tài)的創(chuàng)建一個符合這個簽名的方法,然后執(zhí)行。
消息轉(zhuǎn)發(fā)的第三步可以這么理解:
既然我自己無法處理這個消息,也不知道誰可以處理這個消息,那么我就自己創(chuàng)造一個可以處理這個消息的 NSInvocation.

關(guān)于 NSInvocation 如何自己創(chuàng)造一個對象方法來處理消息轉(zhuǎn)發(fā)
蘋果官方給的解釋是:NSInvocation 作為對象呈現(xiàn)的 Objective-C 消息。
An Objective-C message rendered as an object.
NSInvocation objects are used to store and forward message between objects and between applications,
primarily by NSTimer objects and the distributed objects system.
An NSInvocation object contains all the elements of an Objective-C message: a target ,a selector, arguments , and the return value. Each of these elements can be set directly , and the return value is set automatically when the NSInvocation object is dispatched.
一個 NSInvocation 對象包含一個 Objective-C 消息的所有元素:target、selector、arguments、returnValue。可以直接設(shè)置每個元素,并在 NSInvocation 分派時,自動設(shè)置返回值。
// 一個完整方法OC方法調(diào)用
NSString *returnValue = [p runWithDestination:@"北極熊"];

- target
2.sel
3.arguments
4.returnValue
使用消息轉(zhuǎn)發(fā)的代碼描述則是:
NSString *value = ((id(*)(id,SEL,id))objc_msgSend)(p,sel_registerName("runWithDestination:"),@"北極熊");
NSLog(@"%
OC方法和 C 消息發(fā)送對比
