美文网首页
runtime——方法交换

runtime——方法交换

作者: Aaron升 | 来源:发表于2019-03-02 19:27 被阅读0次

日常开发中,经常会遇到一些难以发现原因的bug。
runtime方法交换可以给我们帮大忙,下面举两个容易理解的场景。
例如 :
NSURL *URL = [NSURL URLWithString:@"https://www.baidu.com/s?ie=UTF-8&wd=图片"];
创建出来的URL用于请求,请求不到数据,你以为是后台的问题,实际却是因为urlString中含有中文导致创建出来的NSURL对象为null

#import "ViewController.h"
@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSURL *URL = [NSURL URLWithString:@"https://www.baidu.com/s?ie=UTF-8&wd=图片"];
    NSURLRequest *request = [NSURLRequest requestWithURL:URL];
    
    NSLog(@"URL = %@",URL); // 当urlString字符串含有中文时,创建出来的NSURL对象为null
}

@end

如何使开发者更容易发现问题所在呢?我们可以在创建完NSURL之后判断一下是否为空,并输入提示信息。
但是我们不希望在每一处使用到该方法的地方写重复的判断代码。
怎么办?
来看看如何使用方法交换:

#import "NSURL+ExchangeImplementations.h"
#import <objc/runtime.h>  // 使用方法交换需要引用此头文件

@implementation NSURL (ExchangeImplementations)
// 程序启动之前会执行此方法
+ (void)load {
    // 获取系统类方法
    Method URLWithString = class_getClassMethod(self, @selector(URLWithString:));
    // 获取自定义类方法
    Method cs_URLWithString = class_getClassMethod(self, @selector(cs_URLWithString:));
    // 调用方法交换
    method_exchangeImplementations(URLWithString, cs_URLWithString);
}
// 实现自定义方法,用来与系统方法URLWithString:交换方法实现
+ (instancetype)cs_URLWithString:(NSString *)URLString {
    NSURL *URL = [NSURL cs_URLWithString:URLString];    // 方法交换后,cs_URLWithString: 会执行系统方法 URLWithString:
    if (!URL) {
        NSLog(@"URL为空");
    }
    return URL;
}
@end

例如还有一个场景,数组越界在我们程序中经常发生,如果没测出来,App上线了,小小的出错可就会影响一大批用户,事情是很严重的。

#import "ViewController.h"
@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSArray *fruits = @[
                     @"apple",
                     @"banner",
                     @"orange",
                     ];
//    NSString *fruit = [fruits objectAtIndex:3];
    NSString *fruit = fruits[3];  // 数组越界,程序会直接崩溃
    NSLog(@"the 3th fruit is %@",fruit);
}

@end

为了将影响降到最低,我们可以利用runtime对NSArray的 objectAtIndex: 和 objectAtIndexedSubscript: 方法做一下安全处理:

#import "NSArray+ExchangeImplementations.h"
#import <objc/runtime.h>

@implementation NSArray (ExchangeImplementations)
// 程序启动之前会执行此方法
+ (void)load {
    
    NSArray *methodNames = @[
                             @"objectAtIndex:",
                             @"objectAtIndexedSubscript:",
                             ];
    
    // 遍历需要做方法交换的方法名数组
    for (NSString *methodName in methodNames) {
        SEL originSEL = NSSelectorFromString(methodName);
        // 自定义方法的方法名比系统方法的方法名前多了 "cs_"
        SEL newSEL = NSSelectorFromString([@"cs_" stringByAppendingString:methodName]);
        // 这里使用 objc_getClass("__NSArrayI") ,是因为 NSArray 是类族, objectAtIndexedSubscript: 是NSArray的子类 __NSArrayI 的实例方法
        Class class = objc_getClass("__NSArrayI");
        Method originMethod = class_getInstanceMethod(class, originSEL);
        Method newMethod = class_getInstanceMethod(class, newSEL);
        // 方法交换
        method_exchangeImplementations(originMethod, newMethod);
    }
}
// 实现自定义方法,用来与系统方法 objectAtIndex: 交换方法实现
- (id)cs_objectAtIndex:(NSUInteger)index {
    // 增加安全判断,防止数组越界带来程序崩溃
    if (index < self.count) {
        return [self cs_objectAtIndex:index];    // 方法交换后, cs_objectAtIndex: 的方法实现会指向 objectAtIndex:
    } else {
        NSLog(@"数组越界了!index = %lu, count = %lu", index, self.count);
        return nil; // 如果下标越界,直接返回nil
    }
}
// 实现自定义方法,用来与系统方法 objectAtIndexedSubscript: 交换方法实现
- (id)cs_objectAtIndexedSubscript:(NSUInteger)idx {
    // 增加安全判断,防止数组越界带来程序崩溃
    if (idx < self.count) {
        return [self cs_objectAtIndexedSubscript:idx];    // 方法交换后, cs_objectAtIndexedSubscript: 的方法实现会指向 objectAtIndexedSubscript:
    } else {
        NSLog(@"数组越界了!index = %lu, count = %lu", idx, self.count);
        return nil; // 如果下标越界,直接返回nil
    }
}
@end

上面的代码中涉及到类族的概念,类族是什么?这是runtime更底层的知识了,想了解更多可以参考二亮子的《为什么object_getClass(obj)与[OBJ class]返回的指针不同》

本文仅举两个例子介绍runtime的方法交换的使用方法及使用场景,什么时候需要使用到runtime的方法交换,这要看开发者如何合理地运用,希望大家能结合实际,举一反三。

相关文章

  • runtime

    runtime交换方法 动态添加方法

  • runTime常用方法

    使用runTime改变实例成员的值 使用runtime来交换两个方法 注意再次调用该方法不交换 使用runTime...

  • Runtime

    runtime运行时机制1:通过runtime,实现方法交换(交换两个类方法、交换两个实例方法)2:通过runti...

  • Day3

    1 runtime运行时机制1:通过runtime,实现方法交换(交换两个类方法、交换两个实例方法)。2:通过ru...

  • runtime的理解(二)

    主要内容 利用 runtime 交换方法 利用 runtime 动态添加方法 利用 runtime 动态添加属性 ...

  • 查看SDK调用支付宝参数

    使用runtime 方法交换openurl

  • objc runtime (四)动态添加属性

    在《objc runtime (二)交换方法》中我提到过runtime最实用的就是交换方法和动态添加属性两个用法。...

  • iOS runtime如何交换两个类方法

    如有转载,请标明出处:iOS runtime如何交换两个类方法 runtime交换实例方法,老生常谈的问题,很多b...

  • iOS -- runtime的应用

    runtime主要有一下几种应用场景 方法交换 添加属性 (一)方法交换 (1)字体适配 方法交换实际交换的是方法...

  • runtime和oc内存区域(2018-04-02)

    runtime常用的几个方法: 交换方法 动态添加属性 动态添加方法 1.交换方法 class_getClassM...

网友评论

      本文标题:runtime——方法交换

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