zoukankan      html  css  js  c++  java
  • iOS开发——高级篇——iOS如何彻底避免数组越界

    我们先来看看有可能会出现的数组越界Crash的地方;

    1
    2
    3
    4
    5
    6
    7
    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
        WelfareItem *item = [_datasourceArray objectAtIndex:indexPath.row];//有可能会越界,你在下拉刷新时会用[_datasourceArray removeAllObjects],这时你又点了某个cell就会Crash
    }
     
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
        WelfareItem *item = _datasourceArray[indexPath.row];//有可能会越界,两个地方用了[tableView reloadData];后一个有[_datasourceArray removeAllObjects];前一个还没有执行完,就会Crash
    }


    上面代码是有可能会越界的;出现Crash也不好复现,发出去的App总是能收到几条Crash;解决这个问题也很简单代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
        WelfareItem *item = nil;
        if (indexPath.row < [_datasourceArray count]) {//无论你武功有多高,有时也会忘记加
            item = [_datasourceArray objectAtIndex:indexPath.row];
        }
    }
     
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
        WelfareItem *item = nil;
        if (indexPath.row < [_datasourceArray count]) {
            item = [_datasourceArray objectAtIndex:indexPath.row];
        }
    }


    问题又来了,无论你武功有多高,有时也会忘记加;所以我们要想一招制敌办法;我是想到了用Runtime把objectAtIndex方法替换一下;代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    /*!
     @category
     @abstract NSObject的Category
     */
    @interface NSObject (Util)
     
    /*!
    @method swizzleMethod:withMethod:error:
    @abstract 对实例方法进行替换
    @param oldSelector 想要替换的方法
    @param newSelector 实际替换为的方法
    @param error 替换过程中出现的错误,如果没有错误为nil
    */
    + (BOOL)swizzleMethod:(SEL)originalSelector withMethod:(SEL)swizzledSelector error:(NSError **)error;
     
    @end
     
     
    #import "NSObject+Util.h"
    #import <objc runtime.h="">
     
    @implementation NSObject (Util)
     
    + (BOOL)swizzleMethod:(SEL)originalSelector withMethod:(SEL)swizzledSelector error:(NSError **)error
    {
        Method originalMethod = class_getInstanceMethod(self, originalSelector);
        if (!originalMethod) {
            NSString *string = [NSString stringWithFormat:@" %@ 类没有找到 %@ 方法",NSStringFromClass([self class]),NSStringFromSelector(originalSelector)];
            *error = [NSError errorWithDomain:@"NSCocoaErrorDomain" code:-1 userInfo:[NSDictionary dictionaryWithObject:string forKey:NSLocalizedDescriptionKey]];
            return NO;
        }
         
        Method swizzledMethod = class_getInstanceMethod(self, swizzledSelector);
        if (!swizzledMethod) {
            NSString *string = [NSString stringWithFormat:@" %@ 类没有找到 %@ 方法",NSStringFromClass([self class]),NSStringFromSelector(swizzledSelector)];
            *error = [NSError errorWithDomain:@"NSCocoaErrorDomain" code:-1 userInfo:[NSDictionary dictionaryWithObject:string forKey:NSLocalizedDescriptionKey]];
            return NO;
        }
         
        if (class_addMethod(self, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))) {
            class_replaceMethod(self, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
        }
        else {
            method_exchangeImplementations(originalMethod, swizzledMethod);
        }
         
        return YES;
    }
     
    @end
     
     
    @implementation NSArray (ErrerManager)
     
    + (void)load
    {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            @autoreleasepool
            {
                [objc_getClass("__NSArrayI") swizzleMethod:@selector(objectAtIndex:) withMethod:@selector(swizzleObjectAtIndex:) error:nil];
                [objc_getClass("__NSArrayM") swizzleMethod:@selector(objectAtIndex:) withMethod:@selector(swizzleObjectAtIndex:) error:nil];
            };
        });
    }
     
    - (id)swizzleObjectAtIndex:(NSUInteger)index
    {
        if (index < self.count)
        {
            return [self swizzleObjectAtIndex:index];
        }
        NSLog(@"%@ 越界",self);
        return nil;//越界返回为nil
    }
     
    @end</objc>


    有了上面代码我们用 [_datasourceArray objectAtIndex:indexPath.row] 就不会发生越界Crash了;越界
    了会返回nil;看来是一个比较不错的解决方案;把app发出去吧,结果我们Crash比之前高了好几倍(越界的Crash没有了,出新的Crash了);Crash如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    1 tbreader 0x002b93e9 tbreader + 2098153
    2 libsystem_platform.dylib 0x33a66873 _sigtramp + 34
    3 libsystem_blocks.dylib 0x33941ae1 _Block_release + 216
    4 libobjc.A.dylib 0x333c11a9 + 404
    5 CoreFoundation 0x25ba23a9 _CFAutoreleasePoolPop + 16
    6 UIKit 0x2912317f + 42
    7 CoreFoundation 0x25c565cd + 20
    8 CoreFoundation 0x25c53c8b + 278
    9 CoreFoundation 0x25c54093 + 914
    10 CoreFoundation 0x25ba2621 CFRunLoopRunSpecific + 476
    11 CoreFoundation 0x25ba2433 CFRunLoopRunInMode + 106
    12 GraphicsServices 0x2cf0a0a9 GSEventRunModal + 136
    13 UIKit 0x2918c809 UIApplicationMain + 1440

    都是这个Crash,出现在iOS7以上(含iOS7),关键还没有用户反馈有问题,Crash高了几倍没有一个用户反馈这种情况还是少见的,大家测试还复现不了;测试了一周终于复现了一样的Crash;是这样出现的,替换了objectAtIndex方法有输入的地方出来了软键盘按手机Home键就Crash了;此法不行只,只能另寻他策了。后来我们就给数组新增扩展方法代码如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    @interface NSArray (SHYUtil)
     
    /*!
     @method objectAtIndexCheck:
     @abstract 检查是否越界和NSNull如果是返回nil
     @result 返回对象
     */
    - (id)objectAtIndexCheck:(NSUInteger)index;
     
    @end
     
     
    #import "NSArray+SHYUtil.h"
     
    @implementation NSArray (SHYUtil)
     
    - (id)objectAtIndexCheck:(NSUInteger)index
    {
        if (index >= [self count]) {
            return nil;
        }
         
        id value = [self objectAtIndex:index];
        if (value == [NSNull null]) {
            return nil;
        }
        return value;
    }
     
    @end

    把之前的代码 WelfareItem *item = [_datasourceArray objectAtIndex:indexPath.row] 改为 WelfareItem *item = [_datasourceArray objectAtIndexCheck:indexPath.row] 就可以了。这样就可以彻底解决数组越界 -[__NSArrayI objectAtIndex:]: index 100 beyond bounds [0 .. 1]' 错误了

  • 相关阅读:
    CSS3 target伪类简介
    不用position,让div垂直居中
    css3 在线编辑工具 连兼容都写好了
    a标签伪类的顺序
    oncopy和onpaste
    【leetcode】1523. Count Odd Numbers in an Interval Range
    【leetcode】1518. Water Bottles
    【leetcode】1514. Path with Maximum Probability
    【leetcode】1513. Number of Substrings With Only 1s
    【leetcode】1512. Number of Good Pairs
  • 原文地址:https://www.cnblogs.com/chglog/p/5322946.html
Copyright © 2011-2022 走看看