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

- (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;解决问题也非常easy代码例如以下:

- (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方法替换一下。代码例如以下:

/*!
@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

有了上面代码我们用 [_datasourceArray
objectAtIndex:indexPath.row] 就不会发生越界Crash了。越界

了会返回nil。看来是一个比較不错的解决方式。把app发出去吧,结果我们Crash比之前高了好几倍(越界的Crash没有了,出新的Crash了)。Crash例如以下

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了;此法不行仅仅,仅仅能另寻他策了。

后来我们就给数组新增扩展方法代码例如以下

@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 数组越界 Crash加工经验的更多相关文章

  1. iOS数组越界

    数组越界就是假如你的下标总数现在为32个,然后你在下一秒又执行了一个方法要从50个数据里进行赋值啊筛选之类的,而你此时数组里的值为32个,50的数据还没有请求到,往往会出现数组越界的崩溃信息,大概是这 ...

  2. 第17月第7天 iOS 数组越界,防Crash处理

    1. 上面方法已经可以避免crash,为了避免冗余的代码,写一个NSArray的分类,利用runtime替换NSArray的对象方法objectAtIndex:,在这里进行判断,捕获异常: #impo ...

  3. iOS如何彻底避免数组越界

    我们先来看看有可能会出现的数组越界Crash的地方: ? 1 2 3 4 5 6 7 - (void)tableView:(UITableView *)tableView didSelectRowAt ...

  4. iOS开发——高级篇——iOS如何彻底避免数组越界

    我们先来看看有可能会出现的数组越界Crash的地方: ? 1 2 3 4 5 6 7 - (void)tableView:(UITableView *)tableView didSelectRowAt ...

  5. iOS应用的crash日志的分析基础

        Outline如何获得crash日志如何解析crash日志如何分析crash日志     1. iOS策略相关     2. 常见错误标识     3. 代码bug 一.如何获得crash日志 ...

  6. iOS 数组字典操作

    iOS开发中需要大量对dictionary和array进行操作,因此我们需要一种更加安全可靠的操作方法来避免不必要的crash.当然可以通过自定义dictionary 和array重载增删改查的方法来 ...

  7. iOS开发之Crash分析,以及收集

    一  先谈谈iOS的Crash收集方式: 1. APP 发生crash,用户手机手机上肯定会有crash纪录,当然删除了该app,或是删了再装 crash纪录还是没了. 2. 如果用户设置-隐私  同 ...

  8. 墨菲定律与 IndexOutOfBoundsException(数组越界异常)

    今天维护又反馈了一问题过来,查询试卷时报数组越界异常: 2017-02-28 10:45:24,827[ERROR] HttpException[10.32.111.7:60446:2D07867BE ...

  9. Objective-c防止数组越界而崩溃(全局效果)

    数组越界其实是很基本的问题,但是解决起来除了count的判断,还有每个调用的时候都要去判断一遍 对于不明确的数据总会有崩溃的风险 然而 每次调用都判断 那是太累了 so ..runtime&c ...

随机推荐

  1. 游戏文字自动断行需要,还得从 UTF-8 讲起

    UTF-8(8-bit Unicode Transformation Format)是一种针对Unicode的可变长度字符编码,也是一种前缀码. UTF-8使用一至六个字节为每个字符编码(尽管如此,2 ...

  2. Android TextView自动实现省略号

    TextView自带的可以通过 android:ellipsize="end" android:singleLine="true"实现单行省略,  但是当我们需 ...

  3. 如何用AndroidStudio导入github项目

    最近一直在研究AndroidStudio,但是总会有这样那样的问题,特别是在github上看到一个很好地开源项目,想clone下来用用,就会出现很多蛋疼的问题,今天摸索着,结合一些大牛们的建议,轻轻松 ...

  4. Android SeekBar实现音量调节

    SeekBar可以通过滑块的位置来标识数值----而且拖动条允许用户拖动滑块来改变值,因此拖动条通常用于对系统的某种数值进行调节,比如调节音量等. SeekBar允许用户改变拖动条的滑块外观,改变滑块 ...

  5. 存储过程系列之调试存储过程 SQL Server 2005

    在数据库中直接调试  在数据库中直接调试是调试SQL Server 2005的存储过程的最简单的方法. 在Visual Stuido的IDE中你可以选择单步执行存储过程,然后就可以一条语句一条语句地单 ...

  6. Java SE知识点

    Java概述 Java的运行机制 JDK,JRE,JVM Java开发环境搭建 用记事本编写运行一个Hello World的完整过程 如何在Java中使用注释 使用Eclipse开发Java程序 使用 ...

  7. HTML5标签一览

    HTML5标签一览,点击下载 访问密码:1907

  8. HTML5实现在线抓拍

    <!DOCTYPE> <html> <head> <meta http-equiv="Content-Type" content=&quo ...

  9. EventHandlerList z

    写一个类时,有时候会在同一个类上添加很多事件,事件很多的话,是不容易管理的,.NET提供的EventHandlerList可以辅助多个事件的管 理,但不方便的地方是,它不是类型安全的,缺少类型安全,多 ...

  10. HDU 3416 Marriage Match IV dij+dinic

    题意:给你n个点,m条边的图(有向图,记住一定是有向图),给定起点和终点,问你从起点到终点有几条不同的最短路 分析:不同的最短路,即一条边也不能相同,然后刚开始我的想法是找到一条删一条,然后光荣TLE ...