利用消息转发解决对NSNull对象操作导致的崩溃

消息转发,适用于把操作给另外一个类来实现
-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
    NSMethodSignature *signature = [super methodSignatureForSelector:aSelector];
    if (!signature) {
        signature = [self.displayLabel methodSignatureForSelector:aSelector];
    }
    return signature;
}

-(void)forwardInvocation:(NSInvocation *)anInvocation
{
    SEL selector = [anInvocation selector];
    if ([self.displayLabel respondsToSelector:selector]) {
        [anInvocation invokeWithTarget:self.displayLabel];
    }
}

下面例子中,一个UIViewController包含了UIlable 属性 displayLabel, 如果UIViewController 实例调用[instance setText:@"string"]方法,由于类没有实现setText:方法,通过上面两行代码,将会转发,由 displayLabel 实现。

#import "RootVC.h"

@interface RootVC ()

@end

@implementation RootVC

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
    }
    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    self.title = @"Root";
    
    [self addButton];
    // Do any additional setup after loading the view.
}

-(void)addButton
{
    UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
    btn.frame = CGRectMake(20.0, 100.0, 100.0, 40.0);
    [btn setTitle:@"Next" forState:UIControlStateNormal];
    [btn setTitleColor:[UIColor grayColor] forState:UIControlStateNormal];
    [btn addTarget:self action:@selector(naviNext) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:btn];
    
    UILabel *label = [[UILabel alloc]initWithFrame:CGRectMake(20.0, 140.0, 200.0, 30.0)];
    self.displayLabel = label;
    [self.view addSubview:label];
    [label release];
 
    [self setText:@"你好"]; //这里setText是不能被自己接受的,但是应为在下面做了消息转发,最后事件由self.displayLabel 接受了
}

-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
    NSMethodSignature *signature = [super methodSignatureForSelector:aSelector];
    if (!signature) {
        signature = [self.displayLabel methodSignatureForSelector:aSelector];
    }
    return signature;
}

-(void)forwardInvocation:(NSInvocation *)anInvocation
{
    SEL selector = [anInvocation selector];
    if ([self.displayLabel respondsToSelector:selector]) {
        [anInvocation invokeWithTarget:self.displayLabel];
    }
}


-(void)naviNext
{
   
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end
下面是封装的分类
//
//  TestController.m
//  0803test
//
//  Created by Xudongdong on 2017/8/29.
//  Copyright © 2017年 Xudongdong. All rights reserved.
//

#import 

@interface NSNull (InternalNullExtention)

@end
//
//  TestController.m
//  0803test
//
//  Created by Xudongdong on 2017/8/29.
//  Copyright © 2017年 Xudongdong. All rights reserved.
//

//来源于 http://blog.rpplusplus.me/blog/2014/03/28/nsnull-category/

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

#import "NSNull+InternalNullExtention.h"

@implementation NSNull (InternalNullExtention)

- (NSMethodSignature*)methodSignatureForSelector:(SEL)selector
{
    NSMethodSignature* signature = [super methodSignatureForSelector:selector];
    if (!signature) {
        for (NSObject *object in NSNullObjects) {
            signature = [object methodSignatureForSelector:selector];
            if (signature) {
                break;
            }
        }
        
    }
    return signature;
}

- (void)forwardInvocation:(NSInvocation *)anInvocation
{
    SEL aSelector = [anInvocation selector];
    
    for (NSObject *object in NSNullObjects) {
        if ([object respondsToSelector:aSelector]) {
            [anInvocation invokeWithTarget:object];
            return;
        }
    }
    
    [self doesNotRecognizeSelector:aSelector];
}


@end

你可能感兴趣的:(利用消息转发解决对NSNull对象操作导致的崩溃)