runtime-方法调用--未找到方法补救--防止crash

unrecognized selector sent to instance 0x10010c840  
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Foo MissMethod]: unrecognized selector sent to instance 0x10010c840'  
*** Call stack at first throw:  
......  
terminate called after throwing an instance of 'NSException'  

 因为app里经常会有方法未找到unrecognized selector 报crash,所以会加一些保险。

针对这个问题 可以有三个方向可加

1、系统提供兜底方法

首先Objective-C在运行时调用+ resolveInstanceMethod:或+ resolveClassMethod:方法,让你添加方法的实现。如果你添加方法并返回YES,那系统在运行时就会重新启动一次消息发送的过程

举一个简单例子,定义一个类Message,它主要定义一个方法sendMessage,下面就是它的设计与实现:

@implementation Message
- (void)sendMessage:(NSString *)word
{
    NSLog(@"test : send message = %@", word);
}
@end

如果我在viewDidLoad方法中创建Message对象并调用sendMessage方法:

- (void)viewDidLoad {
    [super viewDidLoad];
    Message *message = [Message new];
    [message sendMessage:@"hi,dashen"];
}

控制台会打印以下信息:test : send message = hi,dashen

但现在我将原来sendMessage方法实现给注释掉,覆盖resolveInstanceMethod方法:

#pragma mark - Method Resolution
//- (void)sendMessage:(NSString *)word
//{
//    NSLog(@"test : send message = %@", word);
//}
/// override resolveInstanceMethod or resolveClassMethod for changing sendMessage method implementation

+ (BOOL)resolveInstanceMethod:(SEL)sel
{
    if (sel == @selector(sendMessage:)) {
        class_addMethod([self class], sel, imp_implementationWithBlock(^(id self, NSString *word) {
            NSLog(@"resolveInstanceMethod : send message = %@", word);
        }), "v@*");
    }
    return YES;
}

控制台就会打印以下信息:resolveInstanceMethod : send message = hi,dashen

2、为对象添加方法

如果目标对象实现- forwardingTargetForSelector:方法,系统就会在运行时调用这个方法,只要这个方法返回的不是nil或self,也会重启消息发送的过程,把这消息转发给其他对象来处理。否则,就会继续Normal Fowarding。

继续上面Message类的例子,将sendMessage和resolveInstanceMethod方法注释掉,然后添加forwardingTargetForSelector方法的实现:

#pragma mark - Fast Forwarding
- (id)forwardingTargetForSelector:(SEL)aSelector
{
    if (aSelector == @selector(sendMessage:)) {
        return [ClassTestB new];
    }
    return nil;
}

ClassTestB 是一个实现了sendMessage:的类。

 

3、为方法找对象

最后只有使用Normal Forwarding来进行消息转发。它首先调用methodSignatureForSelector:方法来获取函数的参数和返回值,如果返回为nil,程序会Crash掉,并抛出unrecognized selector sent to instance异常信息。如果返回一个函数签名,系统就会创建一个NSInvocation对象并调用-forwardInvocation:方法。

继续前面的例子,将forwardingTargetForSelector方法注释掉,添加methodSignatureForSelector和forwardInvocation方法的实现:

#pragma mark - Normal Forwarding
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
    NSMethodSignature *methodSignature = [super methodSignatureForSelector:aSelector];
    if (!methodSignature) {
        methodSignature = [NSMethodSignature signatureWithObjCTypes:"v@:*"];
    }
    return methodSignature;
}

- (void)forwardInvocation:(NSInvocation *)anInvocation
{
    MessageForwarding *messageForwarding = [MessageForwarding new];
    if ([messageForwarding respondsToSelector:anInvocation.selector]) {
        [anInvocation invokeWithTarget:messageForwarding];
    }
}

三种方法理论上都可以,但是实际做的时候有一定困难,我们为了防止工程因找不到方法崩溃,会在给nsobject类添加上述三种保护,前提是这三种方法 都需要明确知道方法名称,我们只能在最后一种方法里,debug下加个nsasset报个断言,意义不大。上述三种方法对于理解运行时还是有点好处的。不喜勿喷,因为三种方法 对于捕获崩溃是有用的,修复使不上力。

你可能感兴趣的:(app安全稳定)