消息發(fā)送
[person read:book];
objc_msgSend(person, @selector(read:), book);
objc_msgSend的具體流程如下:
- 通過isa指針找到所屬類
- 查找類的cache列表, 如果沒有則下一步
- 查找類的”方法列表”
- 如果能找到與選擇子名稱相符的方法, 就跳至其實現(xiàn)代碼
- 找不到, 就沿著繼承體系繼續(xù)向上查找
- 如果能找到與選擇子名稱相符的方法, 就跳至其實現(xiàn)代碼
- 找不到, 執(zhí)行”消息轉(zhuǎn)發(fā)”.
原則就是從當(dāng)前類開始查找方法的實現(xiàn)朵夏,找不到就去父類找命黔,父類找不到就去父類的父類找,一直找到根類蓖宦,如果最終還是沒有找到,則執(zhí)行消息轉(zhuǎn)發(fā)性誉。
消息轉(zhuǎn)發(fā)
消息轉(zhuǎn)發(fā)的整個流程為三步汹押。
- 動態(tài)方法解析。
//實例方法
+ (BOOL)resolveInstanceMethod:(SEL)selector;
//類方法
+ (BOOL)resolveClassMethod:(SEL)selector;
- 找備用接收者
- (id)forwardingTargetForSelector:(SEL)selector;
- 消息簽名和消息轉(zhuǎn)發(fā)吆视。
//消息簽名
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector;
//消息轉(zhuǎn)發(fā)
- (void)forwardInvocation:(NSInvocation *)invocation;
如果以上都無法處理消息的話,則會拋出異常酥宴±舶桑可以通過對下面方法的處理避免崩潰。
- (void)doesNotRecognizeSelector:(SEL)aSelector拙寡;
代碼實現(xiàn)例子
在Person類中定一個一個方法- (void)sendMessage:(NSString *)message;授滓,但并沒有去實現(xiàn)這個方法。
在Dog類中定義并實現(xiàn)了了- (void)sendMessage:(NSString *)message;
通過Person類的對象調(diào)用sendMessage:方法倒庵,來模擬實現(xiàn)消息的轉(zhuǎn)發(fā)機(jī)制流程褒墨。
外部調(diào)用
Person *p = [[Person alloc]init];
[p sendMessage:@"哈哈"];
Person頭文件
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface Person : NSObject
- (void)sendMessage:(NSString *)message;
@end
NS_ASSUME_NONNULL_END
Dog文件
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface Dog : NSObject
- (void)sendMessage:(NSString *)message;
@end
NS_ASSUME_NONNULL_END
#import "Dog.h"
@implementation Dog
- (void)sendMessage:(NSString *)message
{
NSLog(@"備用接收者實現(xiàn):%@",message);
}
@end
Person.m文件中的實現(xiàn)。
當(dāng)Person調(diào)用sendMessage方法時擎宝,因為person并沒有實現(xiàn)這個方法郁妈,因此按照繼承樹層級查找查找不到該方法的實現(xiàn)。則會走第一個動態(tài)解析的步驟绍申,在這一步噩咪,我們可以選擇是否動態(tài)的去添加一下這個方法的實現(xiàn)顾彰,從而結(jié)束消息轉(zhuǎn)發(fā)的流程,如果不解決這個問題胃碾,則會繼續(xù)走下一步涨享。
//1.動態(tài)方法解析
//是否要添加這個方法的實現(xiàn),添加后這個方法就會實現(xiàn)仆百,如果不添加則走第二步厕隧,越往后處理這個事件,消耗也是越來越大
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
NSString *methodName = NSStringFromSelector(sel);
if ([methodName isEqualToString:@"sendMessage:"]) {
return class_addMethod([self class],sel, (IMP)sendMessage, "v@:@");
}
return NO;
}
void sendMessage(id self, SEL cmd, NSString *message){
NSLog(@"%@",message);
}
當(dāng)不在動態(tài)添加方法的情況下俄周,消息轉(zhuǎn)發(fā)繼續(xù)想下一步執(zhí)行吁讨,會去尋找備用者,也就是返回另一個類的對象峦朗,用這個對象去解決這個方法的實現(xiàn)問題建丧。
//2.找備用的接收者
//轉(zhuǎn)發(fā)到備用的接受者,讓備用的接受者處理波势。
- (id)forwardingTargetForSelector:(SEL)aSelector
{
NSString *methodName = NSStringFromSelector(aSelector);
if ([methodName isEqualToString:@"sendMessage:"]) {
return [Dog new];
}
//如果沒有找到備用者翎朱,就走繼承樹
return [super forwardingTargetForSelector:aSelector];
}
當(dāng)找不到備用接收者的時候,則會進(jìn)入到第三步尺铣,第三步分為兩個小步拴曲,第一步先進(jìn)行方法簽名,第二步進(jìn)行消息轉(zhuǎn)發(fā)迄埃,將消息轉(zhuǎn)發(fā)給其他對象疗韵。
//3. 1.方法簽名
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
NSString *methodName = NSStringFromSelector(aSelector);
if ([methodName isEqualToString:@"sendMessage:"]) {
return [NSMethodSignature signatureWithObjCTypes:"v@:@"];
}
return [super methodSignatureForSelector:aSelector];
}
//3. 2.消息轉(zhuǎn)發(fā)
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
SEL sel = [anInvocation selector];
Dog *dog = [Dog new];
//如果dog實現(xiàn)了這個方法兑障,則把這個消息轉(zhuǎn)發(fā)給dog侄非,如果沒實現(xiàn),則走繼承樹
if ([dog respondsToSelector:sel]) {
[anInvocation invokeWithTarget:dog];
}else {
[super forwardInvocation:anInvocation];
}
[super forwardInvocation:anInvocation];
}
如果以上都無法處理這個消息流译,則消息無法處理逞怨,會產(chǎn)生崩潰,此時我們重寫下面方法福澡,則會解決崩潰問題叠赦。
// 消息無法處理的情況 處理過后不會崩潰
- (void)doesNotRecognizeSelector:(SEL)aSelector
{
NSLog(@"消息無法處理");
}