美文网首页
iOS Runtime 消息转发

iOS Runtime 消息转发

作者: 再好一点点 | 来源:发表于2019-02-12 20:40 被阅读0次

一:  iOS开发常见的一个崩溃信息就是unrecognized selector sent to instance,这是因为调用了不存在的方法导致的(比如字典当做数组来用,使用了下标.  数组当做字典来用,调用了键值对取值),在所有的崩溃中占有相当大的比例

二: 还有一种占比比较高的闪退就是NULL,本来和后台说好的数据类型不会错,但是呢,总是不如意啊.尤其是php当没有数据的时候字典就变成数组了,这样的话客户端解析一不小心就JJ了

不过不怕,咱有黑科技,接下来请往下看

消息转发流程:

下面先来解决第一种情况

1,首先在ViewController类中创建对象以及调用person的sendMessage方法

@implementation ViewController

- (void)viewDidLoad {

    [super viewDidLoad];

    Person*p = [[Person alloc] init];

    [p1 performSelector:NSSelectorFromString(@"sendMessage") withObject:nil];   

}

@interface Person : NSObject

@end

#import "Person.h"

@implementation Person

@end

Student类:

@interface Student :NSObject

@end

#import "Student.h"

@implementation Student

@end

当 Runtime 系统在 Cache 和类的方法列表(包括父类)中找不到要执行的方法时,Runtime 会调用 resolveInstanceMethod: 或 resolveClassMethod: 来给我们一次动态添加方法实现的机会。我们需要用 class_addMethod 函数完成向特定类添加特定方法实现的操作:

动态方法解析

在Person.m中实现一下方法就可以做到不闪退了.

但是这样做不通用,我们可以搞一个继承自NSObject的类,其他的类都继承与这个类,这样只需要在父类中实现以下方法就可以做到不闪退了.不可在分类中实现此方法,会报错的

v@:中, v表示返回值void, @表示对象self, :表示SEL

//在.m中实现这两个方法:

-(void)noObjMethod{   

    NSLog(@"未实现这个实例方法");

}

+(void)noClassMethod{   

    NSLog(@"未实现这个类方法");

}

//并且重写消息转发的方法:

// 当一个对象调用未实现的方法,会调用这个方法处理,并且会把对应的方法列表传过来.

//注意:实例方法是存在于当前对象对应的类的方法列表中

+(BOOL)resolveInstanceMethod:(SEL)sel{   

    SEL aSel = NSSelectorFromString(@"noObjMethod");   

    Method aMethod = class_getInstanceMethod(self, aSel);   

    class_addMethod([self class], sel, method_getImplementation(aMethod), method_getTypeEncoding(method));   

    return YES;

}

重定向

2,如果resolveInstanceMethod 返回值为NO,会执行- (id)forwardingTargetForSelector:(SEL)aSelector,此方法会将消息转发给Student类实现

- (id)forwardingTargetForSelector:(SEL)aSelector {

    if(aSelector ==@selector(sendMessage:)) {

        return [Student new];

    }else{

        return  [super forwardingTargetForSelector:aSelector];

    }

}

转发

3,如果- (id)forwardingTargetForSelector:(SEL)aSelector返回值为nil,则会调用以下方法

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

    if(aSelector ==@selector(sendMessage:)) {

        return [NSMethodSignature signatureWithObjCTypes:"V@:@"];

    }

    return nil;

}

- (void)forwardInvocation:(NSInvocation*)anInvocation {

    SEL sel = [anInvocation selector];

    Student *stu = [Student new];

    if([stu respondsToSelector:sel]) {

        [anInvocation invokeWithTarget:stu];

    }else{

        [super forwardInvocation:anInvocation];

    }

}

注:- (void)forwardInvocation:(NSInvocation*)anInvocation 方法实现可以将消息转发给多个对象实现

如果以上三种重载都没执行消息,此时会调用- (void)doesNotRecognizeSelector:(SEL)aSelector方法,此时程序会崩溃

- (void)doesNotRecognizeSelector:(SEL)aSelector {

    NSLog(@"doesNotRecognizeSelector");

}

从流程图可以看出,越是往后开销越大,所以在早期做出预防处理是最好的选择

所以直接在父类中重写resolveInstanceMethod方法,就可以做到程序不会崩溃了

接下来解决第二种情况,就是NULL的情况

这时候可以搞一个分类,如下:

#import <Foundation/Foundation.h>

@interface NSNull (Exception)

@end

#import "NSNull+Exception.h"

#import <objc/runtime.h>

@implementation NSNull (Exception)

#define pLog

#define JsonObjects @[@"",@0,@{},@[]]

//在.m中实现这两个方法:

-(void)noObjMethod{

    NSLog(@"未实现这个实例方法");

}

+(void)noClassMethod{

    NSLog(@"未实现这个类方法");

}

+(BOOL)resolveInstanceMethod:(SEL)sel{

    for(id jsonObj in JsonObjects) {

        if([jsonObj respondsToSelector:sel]) {

#ifdef pLog

            NSLog(@"NULL出现啦!这个对象应该是是_%@",[jsonObj class]);

#endif

        }

    }

    SEL aSel = NSSelectorFromString(@"noObjMethod");

    Method aMethod = class_getInstanceMethod(self, aSel);

    class_addMethod([self class], sel, method_getImplementation(aMethod), "v@:");

    return YES;

}

如果此时在 ViewController.m中调用一下错误的字典 则不会引起崩溃

NSDictionary* dict = [[NSNull alloc] init];

[dict objectForKey:@"123"];

控制台会打印出以下信息:

2019-03-02 09:44:30.905674+0800 Test[28763:7642018] NULL出现啦!这个对象应该是是___NSDictionary0

2019-03-02 09:44:30.905790+0800 Test[28763:7642018] 未实现这个实例方法

现在就可以随意折腾了

好了,至此咱们的APP就可以减少大部分闪退问题了

Demo地址:GitHub - yeshibuzhong/iOS 中的Runtime_01

相关文章

网友评论

      本文标题:iOS Runtime 消息转发

      本文链接:https://www.haomeiwen.com/subject/wotleqtx.html