我們先來看看有可能會出現的數組越界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 }
- (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]; } }
/*! @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@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
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]' 錯誤了