一圖勝千言,習慣性的先來一張圖以便對消息轉發有個整體的把握
對于對象無法處理的消息,如果不做轉發處理的話,程序最終會調用NSObjective的doesNotRecognizeSelector:消息將程序crash掉。
如果你擁有了一個定義了對象能夠消化哪些消息的目標類,快速轉發可以取得很好的效果。如果你沒有這樣目標類或想要執行其他處理過程(如記錄日志并‘吞下’消息),就應該使用完整轉發。
下面我要把Test實例的logName消息轉發給Target實例,代碼如下 Test頭文件
//// Test.h// ForwardMsg//// Created by 孫磊 on 2017/2/25.// Copyright ? 2017年 孫磊. All rights reserved.//#import <Foundation/Foundation.h>@interface Test : NSObject-(void)logName;@endTest實現文件
//// Test.m// ForwardMsg//// Created by 孫磊 on 2017/2/25.// Copyright ? 2017年 孫磊. All rights reserved.//#import "Test.h"#import "Target.h"#import <objc/runtime.h>@implementation Test{ Target *mTarget;}- (instancetype)init{ self = [super init]; if (self) { //創建目標對象 mTarget = [Target new]; } return self;}#if 0//當一個對象無法識別消息后,會執行resolveInstanceMethod或者resolveClassMethod方法//如果不想進行消息轉發,可以在此方法中動態添加消息來做處理//如果不重寫此方法或者此方法返回NO,系統會執行forwardingTargetForSelector進行快速轉發+ (BOOL)resolveInstanceMethod:(SEL)sel{ if(sel == @selector(logName)){ //第四個參數詳解地址 https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html //v代表返回類型為void //@代表一個對象 //:代表一個selector //因為OC中的每個方法都有默認的兩個參數sel 和 selector,所以一般都是v@: class_addMethod([self class],sel,(IMP)dynamicMethodIMP,"v@:"); return YES; } return [super resolveInstanceMethod:sel];}//萬年備胎void dynamicMethodIMP(id self, SEL _cmd){ //對無法識別的消息做處理 NSLog(@"該對象無法識別 %@ 方法------%s", NSStringFromSelector(_cmd),__func__);}#else /***************==========1、快速消息轉發,快速轉發只可以獲取到方法簽名==========*******************/-(id)forwardingTargetForSelector:(SEL)aSelector{ NSLog(@"%s",__func__); if ([mTarget respondsToSelector:aSelector]) { //目標對象有對應的處理方法,則就會快速消息轉發,不會再執行完整消息轉發了 return mTarget; } //目標對象也沒有對應的方法,此時系統會執行forwardInvocation進行完整消息轉發 return nil;}/***********=============2、標準(完整)消息轉發,完整消息轉發,可以獲取方法簽名,參數等詳細信息==========*********///返回一個完整的方法簽名,提供給forwardInvocation以便完整轉發消息-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{ NSMethodSignature* signature = [super methodSignatureForSelector:aSelector]; if (!signature) signature = [mTarget methodSignatureForSelector:aSelector]; return signature;}-(void)forwardInvocation:(NSInvocation *)anInvocation{ NSLog(@"%s-----完整消息轉發------",__func__); SEL invSEL = anInvocation.selector; if ([mTarget respondsToSelector:invSEL]){ //利用forwardInvocation方法來重新指定消息處理對象 [anInvocation invokeWithTarget:mTarget]; } else { [self doesNotRecognizeSelector:invSEL]; }}#endif@end目標文件的頭文件
//// Target.h// ForwardMsg//// Created by 孫磊 on 2017/2/25.// Copyright ? 2017年 孫磊. All rights reserved.//#import <Foundation/Foundation.h>@interface Target : NSObject-(void)logName;@end目標文件的實現文件
//// Target.m// ForwardMsg//// Created by 孫磊 on 2017/2/25.// Copyright ? 2017年 孫磊. All rights reserved.//#import "Target.h"@implementation Target-(void)logName{ NSLog(@"我是備用方法---%s",__func__);}@end推薦一個國外大大利用消息轉發避免后臺返回null或者NSNull而引起的奔潰問題,用法也很簡單,直接把NullSafe.m拖到項目中即可,該文件會在運行時自動加載
新聞熱點
疑難解答