iOS开发之原来Runtime的黑魔法这么厉害

学习iOS以来不间断的会听到Runtime,开始感觉OC的语法已经很完善了根本没有必要学习它背后的运行机制,一直以来对这块知识点置若罔闻的我,今天竟然认认真真的读了一篇Runtime的文章,然后就被它深深地吸引住了^_^


runtime.png

今天在这里小菜鸟不会罗列Runtime的语法,但会通过一个简单的Runtime实例的认真讲解让大家伙领会Runtime的魅力所在。

1、实例内容:通过Runtime来阻止数组越界

2、应用场景:开发程序过程中大家多多少少都会遇到数组越界的情况

NSArray *array=[[NSArray alloc]initWithObjects:@"111",@"222", nil]; 

[array objectAtIndex:3];

遇到上述用法直接导致的后果就是我们的程序会崩掉,在开发中遇到我们还可以修改程序,但用户实际使用时崩掉就会闪退,这会使用户体验大大折扣的。

那有没有一种可能在越界的时候,如果我们处于调试过程就让它跳出错误,而当用户使用时越界就不让它崩溃呢。答案是肯定的,那接下来小菜鸟就带领大家一起领略Runtime的黑魔法。

3、Runtime的黑魔法讲解
首先我们要明确下需求:

  • 1、调试过程中越界提示错误
  • 2、用户实际使用过程中数组越界不影响正常使用
  • 3、如果程序开发中修改,之前使用的objectAtIndex:不受影响

下面是我写的一个NSArray的类别,代码:

@implementation NSArray (Boundary)

+(void)load{

  static dispatch_once_t onceToken;
      dispatch_once(&onceToken, ^{
      SEL safeSel=@selector(safeObjectAtIndex:);
      SEL unsafeSel=@selector(objectAtIndex:);

      Class myClass = NSClassFromString(@"__NSArrayI");
      Method safeMethod=class_getInstanceMethod (myClass, safeSel);
      Method unsafeMethod=class_getInstanceMethod (myClass, unsafeSel);

      method_exchangeImplementations(unsafeMethod, safeMethod);

 });
}
-(id)safeObjectAtIndex:(NSUInteger)index{

    if (index>(self.count-1)) {
        NSAssert(NO, @"beyond the boundary");
        return nil;
    }
    else{
        return [self safeObjectAtIndex:index];
    }
}
@end

这些代码什么意思呢,小菜鸟接下来会分段讲解的。

+(void)load

这个方法是在创建分类的时候系统会自动调用的方法

static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
});

这句代码我们单利中常常会用到,保证代码只执行一次就不多做解释了。

 SEL safeSel=@selector(safeObjectAtIndex:);
 SEL unsafeSel=@selector(objectAtIndex:);

objc_selector结构体的详细定义没有在头文件中找到。方法的selector用于表示运行时方 法的名字。Objective-C在编译时,会依据每一个方法的名字、参数序列,生成一个唯一的整型标识(Int类型的地址),这个标识就是SEL。

 Method safeMethod=class_getInstanceMethod (myClass, safeSel);
 Method unsafeMethod=class_getInstanceMethod (myClass, unsafeSel);

使用runtime方法拿到实例中的方法

method_exchangeImplementations(unsafeMethod, safeMethod);

交换SEL对应的IMP实现。

总的来说我们就是将objectAtIndex:和safeObjectAtIndex:两个函数互换了,也就是说我们在应用中如果调用objectAtIndex:实际上我们调用的是safeObjectAtIndex:这个方法。

这里还要补充一点的就是在safeObjectAtIndex:中我们写了这么一句return [self safeObjectAtIndex:index];是什么意思呢,难道不会循环调用?当然不会!

当我们外部调用objectAtIndex:时才会调用safeObjectAtIndex:,而return中调用safeObjectAtIndex:实际上调用的就是objectAtIndex:。哈哈感觉有点饶了,其实就是两个函数已经通过Runtime互换了。

NSAssert(NO, @"beyond the boundary");

在方法-(id)safeObjectAtIndex:(NSUInteger)index中用到的这个就是OC中的断言,简单讲就是当程序处于debug模式时会给我们跑出错误信息,而处于Release模式时也就是用户使用程序时不会抛出信息而导致闪退。

到这里我们就完成了防止数组越界闪退,内容很简单,有什么不懂得地方小伙伴们可以留言询问。

后续还会更新更多Runtime的实例,喜欢就点喜欢吧^_^



    文/逆袭的小菜鸟(简书作者)
    原文链接:http://www.jianshu.com/p/d26536d39ab0
    著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。
    • 0
      点赞
    • 1
      收藏
      觉得还不错? 一键收藏
    • 0
      评论

    “相关推荐”对你有帮助么?

    • 非常没帮助
    • 没帮助
    • 一般
    • 有帮助
    • 非常有帮助
    提交
    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包
    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值