runtime 容错机制一:消息的决议和转发

来源:互联网 发布:极简主义 知乎 编辑:程序博客网 时间:2024/05/17 19:17

在代码中,调用了一个声明但是未被实现的方法的时候(包括定义一个btn的点击事件,但是未实现),程序会奔溃,控制台提示:unrecognized selector sent to instance 'xxxxxxxxx'.

解决方案:利用runtime的消息机制。

定义一个testModel。

testModel.h中声明一个方法

@interface testModel : NSObject- (NSString *)testModel:(NSString *)txt;//- (void)testSecondModel;@end

testModel.m中

可以分为3步走:

1. 动态方法决议。objc运行时会调用

+ (BOOL)resolveInstanceMethod:(SEL)sel;//调用对象方法执行
+ (BOOL)resolveClassMethod:(SEL)sel;//调用类方法执行

让程序提供一个(本类中的函数)函数实现。如果你返回的是YES,运行时系统就会被重新启动一次消息发送的过程,如果返回的是NO,运行时会执行下一步,即消息转发(Message Forwording)。

CODE:

void testTest(id self,SEL _cmd) {    NSLog(@"自动替换成了这个方法的实现");}@implementation testModel- (instancetype)init {    if (self = [super init]) {    }    return self;}+ (BOOL)resolveInstanceMethod:(SEL)sel {    if (sel == @selector(testModel:)) {        class_addMethod([self class], sel, (IMP)testTest, "v@:");//"v@:"看后面的注解        return true;    }    return [super resolveInstanceMethod:sel];}+ (BOOL)resolveClassMethod:(SEL)sel {    return [super resolveClassMethod:sel];}

当运行时根据方法名从当前类和父类中找不到方法的实现的时候,会执行决议方法,通过class_addMethod手动调用一个函数实现,从而避免crash。

2.如果动态决议未实现或者return NO,那么进入消息转发 Fast Forwording

在目标类中实现

- (id)forwardingTargetForSelector:(SEL)aSelector; 

这个方法是可以把消息转发给另外其他对象,运行时会根据方法名去另外对象的方法列表中查找对应方法的实现。注意:调用这个方法的返回值不能是self或者nil,一般是其他的对象。

创建另外一个testSecondModel类:

不用在.h中声明,直接在.m中实现

#import "testSecondModel.h"@implementation testSecondModel- (NSString *)testModel:(NSString *)txt {    return @"1234";}@end

这样,方法的实现就被转移到了testSecondModel中来。

3. 最后一步,Normal forwording。

这一步是runtime最后一次挽救的机会,首先他会发送-methodSignatureForSelector:消息的函数的参数和返回值类型,如果-methodSignatureForSelector:返回nil,runtime则会发出-doesNotRecognizeSelector:消息,程序这个时候就奔溃了。如果返回了一个函数签名,runtime就会创建一个NSInvocation对象并发送-forwardingInvocation:消息给目标对象。

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector;

获得函数的参数和返回值类型,如果返回nil,runtime则会发出:

- (void)doesNotRecognizeSelector:(SEL)aSelector;//抛出异常,程序Crash。理论上可以重写该方法避免程序奔溃,但是不建议这样做。

如果不是返回的nil,而是一个函数签名,runtime就会创建一个NSInvocation并发送:

- (void)forwardInvocation:(NSInvocation *)invocation;

在这个函数里把消息传递给其他对象。

CODE:

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {    NSString *sel = NSStringFromSelector(aSelector);    if (sel) {        return [NSMethodSignature signatureWithObjCTypes:"v@:"];    }    return [super methodSignatureForSelector:aSelector];}- (void)forwardInvocation:(NSInvocation *)invocation {    //拿到函数名    NSString *key = NSStringFromSelector([invocation selector]);    if (key) {        testSecondModel *second = [[testSecondModel alloc]init];        [invocation invokeWithTarget:second];    }}


注释:

void testTest(id self,SEL _cmd) {    }

"v@:"  代表的是函数的返回类型void。

v :    函数的返回值

@: 参数

::方法


int say(id self, SEL _cmd, NSString *str) { }

"i@:@"

i :代表函数的返回类型Int

@:代表函数的参数id self

::代表函数的方法  SEL _cmd

@:代表函数的参数  NSString *str;


原创粉丝点击