一图胜千言,习惯性的先来一张图以便对消息转发有个整体的把握
对于对象无法处理的消息,如果不做转发处理的话,程序最终会调用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拖到项目中即可,该文件会在运行时自动加载
新闻热点
疑难解答