iOS 数组越界 Crash处理经验

我们先来看看有可能会出现的数组越界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;解决这个问题也很简单代码如下:- (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 + 20981532 libsystem_platform.dylib 0x33a66873 _sigtramp + 343 libsystem_blocks.dylib 0x33941ae1 _Block_release + 2164 libobjc.A.dylib 0x333c11a9 + 4045 CoreFoundation 0x25ba23a9 _CFAutoreleasePoolPop + 166 UIKit 0x2912317f + 427 CoreFoundation 0x25c565cd + 208 CoreFoundation 0x25c53c8b + 2789 CoreFoundation 0x25c54093 + 91410 CoreFoundation 0x25ba2621 CFRunLoopRunSpecific + 47611 CoreFoundation 0x25ba2433 CFRunLoopRunInMode + 10612 GraphicsServices 0x2cf0a0a9 GSEventRunModal + 13613 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处理经验

相关文章:

你感兴趣的文章:

标签云: