我们先来看看有可能会出现的数组越界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]' 错误了

iOS开发——高级篇——iOS如何彻底避免数组越界的更多相关文章

  1. iOS开发——高级篇——iOS开发之网络安全密码学

    一.非对称加密 - RSA : + 公钥加密,私钥解密: + 私钥加密,公钥解密: + 只能通过因式分解来破解 二.对称加密 - DES - 3DES - AES (高级密码标准,美国国家安全局使用, ...

  2. iOS开发——高级篇——iOS 项目的目录结构

    最近闲来无事去面试一下iOS开发,让我感到吃惊的,面试官竟然问怎么分目录结构,还具体问每个子目录的文件名. 目录结构确实非常重要,面试官这么问,无疑是想窥探开发经验.清晰的目录结构,可让人一眼明白相应 ...

  3. iOS开发——高级篇——iOS中如何选择delegate、通知、KVO(以及三者的区别)

      在开发IOS应用的时候,我们会经常遇到一个常见的问题:在不过分耦合的前提下,controllers[B]怎么进行通信.在IOS应用不断的出现三种模式来实现这种通信:1委托delegation2通知 ...

  4. iOS开发——高级篇——iOS 强制退出程序APP代码

    1.先po代码 UIAlertView* alert = [[UIAlertView alloc] initWithTitle:self.exitapplication message:@" ...

  5. iOS开发——高级篇——iOS中常见的设计模式(MVC/单例/委托/观察者)

    关于设计模式这个问题,在网上也找过一些资料,下面是我自己总结的,分享给大家 如果你刚接触设计模式,我们有好消息告诉你!首先,多亏了Cocoa的构建方式,你已经使用了许多的设计模式以及被鼓励的最佳实践. ...

  6. iOS开发——高级篇——iOS中为什么block用copy属性

    1. Block的声明和线程安全Block属性的声明,首先需要用copy修饰符,因为只有copy后的Block才会在堆中,栈中的Block的生命周期是和栈绑定的,可以参考之前的文章(iOS: 非ARC ...

  7. iOS开发——高级篇——iOS键盘的相关设置(UITextfield)

    一.键盘风格 UIKit框架支持8种风格键盘. typedef enum { UIKeyboardTypeDefault, // 默认键盘:支持所有字符 UIKeyboardTypeASCIICapa ...

  8. iOS开发——高级篇——iOS 中的 NSTimer

    以前的老代码在使用 NSTimer 时出现了内存泄露 NSTimer fire 我们先用 NSTimer 来做个简单的计时器,每隔5秒钟在控制台输出 Fire .比较想当然的做法是这样的: 1 2 3 ...

  9. iOS开发——高级篇——iOS涂鸦画板效果实现

    一个简单的绘图应用,模仿苹果自带软件备忘录里的涂鸦功能 核心代码 #import "DrawView.h" #import "DrawPath.h" @inte ...

  10. iOS开发——高级篇——iOS抽屉效果实现原理

    实现一个简单的抽屉效果: 核心思想:KVO实现监听mainV的frame值的变化 核心代码: #import "ViewController.h" // @"frame& ...

随机推荐

  1. live555简介

    live555 编辑   目录 1live555简介 2Live555 Streaming Media整体框架 3openRTSP客户端流程     1live555简介编辑 Live555 是一个为 ...

  2. HDFS的Java API 对文件的操作

    在本次操作中所用到的命令 1.首先启动HDFS $HADOOP_HOME/sbin/start-dfs.sh 2.关防火墙 切换到root用户,执行service iptables stop 3.拷贝 ...

  3. impdp导入

    //导入命令impdp 用户/密码@数据库链接 directory=数据库中映射路径 schemas=原用户 remap_schema=原用户:现用户 remap_tablespace=原表空间:现表 ...

  4. stl sort和qsort的使用

    好不容易使用了下stl的qsort函数,顺便和sort函数一起总结下: 很多时候我们都需要用到排序. 例如: 1 #include <iostream> #include <algo ...

  5. 版本控制git之五-标签管理 tags 标签 代码版本 如: v1.0

      版本控制git之五-标签管理 打标签 像其他版本控制系统(VCS)一样,Git 可以给历史中的某一个提交打上标签,以示重要. 比较有代表性的是人们会使用这个功能来标记发布结点(v1.0 等等). ...

  6. Courses on Turbulence

    Courses on Turbulence Table of Contents 1. Lecture 1.1. UIUC Renewable energy and turbulent environm ...

  7. nodejs的express框架创建https服务器

    一 openssl创建https私钥和证书 1.下载windows版openssl: http://slproweb.com/products/Win32OpenSSL.html Win64OpenS ...

  8. ..net 3.5新特性之用this关键字为类添加扩展方法

    具体用法如下: public static class ClassHelper { //用this 声明将要吧这个方法附加到Student对象 public static bool CheckName ...

  9. 缩小Oracle目录下UNDOTBS01.DBF文件的大小

    缩小Oracle目录下UNDOTBS01.DBF文件的大小 分类: Oracle 使用sys用户登录Oracle 方法一:重置表空间大小 执行ALTER DATABASE DATAFILE 'D:OR ...

  10. IDEA-基本设置

    目录: 1.设置内存 2.设置编码格式 3.设置换行符 4.设置新建Class文档说明 5.添加自定义注释 6.设置自己的maven 工欲善其事,必先利其器,设置好基础的设置才能事半功倍!少踩坑!以下 ...