美文网首页iOS开发攻城狮的集散地iOS性能优化iOS底层技术
Runtime解决NSArray数组越界崩溃问题分析

Runtime解决NSArray数组越界崩溃问题分析

作者: wg刚 | 来源:发表于2018-04-20 16:48 被阅读91次

    项目中经常会出现数组越界的崩溃,较好的一种解决方案是通过runtime来解决。不过如果考虑不全面的话依然会出现越界崩溃情况。
    下面将一步一步根据代码结果讲解:

    一、数组越界崩溃日志

    注意:这里会分为三种,很多人只会注意到其中一到两种。

    1. 也是最常见的,数组有大于1个数据。
        NSArray *arr = @[@"1",@"2",@"3"];
        NSString *str = [arr objectAtIndex:5];
    
    2018-04-20 15:50:42.709566+0800 SwiftStudyFour[13453:866758] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayI objectAtIndex:]: index 5 beyond bounds [0 .. 2]'
    
    我不加下面这个图的话,还会有人找不到重点,😑🙂
    image.png
    1. 数组只有1个数据
        NSArray *arr = @[@"1"];
        NSString *str = [arr objectAtIndex:5];
    
    2018-04-20 16:07:58.753246+0800 SwiftStudyFour[13632:995238] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSSingleObjectArrayI objectAtIndex:]: index 5 beyond bounds [0 .. 0]'
    
    image.png
    1. 数组中0个数据
        NSArray *arr = @[];
        NSString *str = [arr objectAtIndex:5];
    
    2018-04-20 16:15:16.649412+0800 SwiftStudyFour[13783:1050271] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArray0 objectAtIndex:]: index 5 beyond bounds for empty NSArray'
    
    image.png

    有这三中图可以看看出,根据数组中内容数量不同造成数组崩溃的系统交换方法也不一样。

    二 runtime 获取类名
        NSArray *commonArr = [NSArray alloc];
        NSArray *arr01 = [commonArr init];
        NSArray *arr02 = [commonArr initWithObjects:@"1", nil];
        NSArray *arr03 = [commonArr initWithObjects:@"1", @"2", nil];
        NSArray *arr04 = [commonArr initWithObjects:@"1", @"2", @"3", nil];
        NSLog(@"commonArr: %s", object_getClassName(commonArr));
        NSLog(@"arr01: %s", object_getClassName(arr01));
        NSLog(@"arr02: %s", object_getClassName(arr02));
        NSLog(@"arr03: %s", object_getClassName(arr03));
        NSLog(@"arr04: %s", object_getClassName(arr04));
    
    2018-04-20 16:26:34.251071+0800 SwiftStudyFour[13953:1133644] commonArr: __NSPlaceholderArray
    2018-04-20 16:26:34.251140+0800 SwiftStudyFour[13953:1133644] arr01: __NSArray0
    2018-04-20 16:26:34.251226+0800 SwiftStudyFour[13953:1133644] arr02: __NSSingleObjectArrayI
    2018-04-20 16:26:34.251384+0800 SwiftStudyFour[13953:1133644] arr03: __NSArrayI
    2018-04-20 16:26:34.251537+0800 SwiftStudyFour[13953:1133644] arr04: __NSArrayI
    

    看到打印结果是不是和在崩溃时标注的类一致。

    三 方法交换解决越界崩溃

    根据上述分析,如果只做如下代码的话是不能拦截所有崩溃的,哪种情况会崩溃可根据上述所说自己试验一下🙂

    +(void)load{
        [super load];
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
    //        __NSArrayI
            Method old = class_getInstanceMethod(objc_getClass("__NSArrayI"), @selector(objectAtIndex:));
            Method new = class_getInstanceMethod(self, @selector(WG_objectAtIndexI:));
            if (old && new) {
                method_exchangeImplementations(old, new);
            }
        });
    }
    
    - (id)WG_objectAtIndexI:(NSUInteger)index {
        if (index > self.count - 1 || !self.count) {
            @try {
                return [self WG_objectAtIndexI:index];
            } @catch (NSException *exception) {
                NSLog(@"不可数组越界了");
                return nil;
            } @finally {
            }
        } else {
            return [self WG_objectAtIndexI:index];
        }
    }
    

    正确交换方法如下:

    +(void)load{
        [super load];
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            Method old = class_getInstanceMethod(objc_getClass("__NSArrayI"), @selector(objectAtIndex:));
            Method new = class_getInstanceMethod(self, @selector(WG_objectAtIndex_NSArrayI:));
            if (old && new) {
                method_exchangeImplementations(old, new);
            }
            
            old = class_getInstanceMethod(objc_getClass("__NSSingleObjectArrayI"), @selector(objectAtIndex:));
            new = class_getInstanceMethod(objc_getClass("__NSSingleObjectArrayI"), @selector(WG_objectAtIndex_NSSingleObjectArrayI:));
            if (old && new) {
                method_exchangeImplementations(old, new);
            }
            
            old = class_getInstanceMethod(objc_getClass("__NSArray0"), @selector(objectAtIndex:));
            new = class_getInstanceMethod(objc_getClass("__NSArray0"), @selector(WG_objectAtIndex_NSArray0:));
            if (old && new) {
                method_exchangeImplementations(old, new);
            }
            
        });
    }
    
    - (id)WG_objectAtIndex_NSArrayI:(NSUInteger)index {
        if (index > self.count - 1 || !self.count) {
            @try {
                return [self WG_objectAtIndex_NSArrayI:index];
            } @catch (NSException *exception) {
                NSLog(@"不可数组多元素越界了");
                return nil;
            } @finally {
            }
        } else {
            return [self WG_objectAtIndex_NSArrayI:index];
        }
    }
    
    - (id)WG_objectAtIndex_NSSingleObjectArrayI:(NSUInteger)index {
        if (index > self.count - 1 || !self.count) {
            @try {
                return [self WG_objectAtIndex_NSSingleObjectArrayI:index];
            } @catch (NSException *exception) {
                NSLog(@"不可数组一个元素越界了");
                return nil;
            } @finally {
            }
        } else {
            return [self WG_objectAtIndex_NSSingleObjectArrayI:index];
        }
    }
    
    - (id)WG_objectAtIndex_NSArray0:(NSUInteger)index {
        if (index > self.count - 1 || !self.count) {
            @try {
                return [self WG_objectAtIndex_NSArray0:index];
            } @catch (NSException *exception) {
                NSLog(@"不可数组0个元素越界了");
                return nil;
            } @finally {
            }
        } else {
            return [self WG_objectAtIndex_NSArray0:index];
        }
    }
    

    实验代码:

        NSArray *arr1 = @[];
        NSString *str1 = [arr1 objectAtIndex:5];
        
        NSArray *arr2 = @[@"1"];
        NSString *str2 = [arr2 objectAtIndex:5];
        
        NSArray *arr3 = @[@"1", @"2", @"3"];
        NSString *str3 = [arr3 objectAtIndex:5];
    

    结果:不会崩溃了,打印日志如下

    2018-04-20 16:47:16.717812+0800 CustomLearn[14152:1183376] 不可数组0个元素越界了
    2018-04-20 16:47:16.718006+0800 CustomLearn[14152:1183376] 不可数组一个元素越界了
    2018-04-20 16:47:16.718197+0800 CustomLearn[14152:1183376] 不可数组多元素越界了
    

    相关文章

      网友评论

        本文标题:Runtime解决NSArray数组越界崩溃问题分析

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