项目中经常会出现数组越界的崩溃,较好的一种解决方案是通过runtime来解决。不过如果考虑不全面的话依然会出现越界崩溃情况。
下面将一步一步根据代码结果讲解:
一、数组越界崩溃日志
注意:这里会分为三种,很多人只会注意到其中一到两种。
- 也是最常见的,数组有大于1个数据。
NSArray *arr = @[@"1",@"2",@"3"];
NSString *str = [arr objectAtIndex:5];
2018-04-20 15:50:42.709566+0800 SwiftStudyFour[13453:866758] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayI objectAtIndex:]: index 5 beyond bounds [0 .. 2]'
我不加下面这个图的话,还会有人找不到重点,😑🙂
image.png- 数组只有1个数据
NSArray *arr = @[@"1"];
NSString *str = [arr objectAtIndex:5];
2018-04-20 16:07:58.753246+0800 SwiftStudyFour[13632:995238] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSSingleObjectArrayI objectAtIndex:]: index 5 beyond bounds [0 .. 0]'
image.png
- 数组中0个数据
NSArray *arr = @[];
NSString *str = [arr objectAtIndex:5];
2018-04-20 16:15:16.649412+0800 SwiftStudyFour[13783:1050271] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArray0 objectAtIndex:]: index 5 beyond bounds for empty NSArray'
image.png
有这三中图可以看看出,根据数组中内容数量不同造成数组崩溃的系统交换方法也不一样。
二 runtime 获取类名
NSArray *commonArr = [NSArray alloc];
NSArray *arr01 = [commonArr init];
NSArray *arr02 = [commonArr initWithObjects:@"1", nil];
NSArray *arr03 = [commonArr initWithObjects:@"1", @"2", nil];
NSArray *arr04 = [commonArr initWithObjects:@"1", @"2", @"3", nil];
NSLog(@"commonArr: %s", object_getClassName(commonArr));
NSLog(@"arr01: %s", object_getClassName(arr01));
NSLog(@"arr02: %s", object_getClassName(arr02));
NSLog(@"arr03: %s", object_getClassName(arr03));
NSLog(@"arr04: %s", object_getClassName(arr04));
2018-04-20 16:26:34.251071+0800 SwiftStudyFour[13953:1133644] commonArr: __NSPlaceholderArray
2018-04-20 16:26:34.251140+0800 SwiftStudyFour[13953:1133644] arr01: __NSArray0
2018-04-20 16:26:34.251226+0800 SwiftStudyFour[13953:1133644] arr02: __NSSingleObjectArrayI
2018-04-20 16:26:34.251384+0800 SwiftStudyFour[13953:1133644] arr03: __NSArrayI
2018-04-20 16:26:34.251537+0800 SwiftStudyFour[13953:1133644] arr04: __NSArrayI
看到打印结果是不是和在崩溃时标注的类一致。
三 方法交换解决越界崩溃
根据上述分析,如果只做如下代码的话是不能拦截所有崩溃的,哪种情况会崩溃可根据上述所说自己试验一下🙂
+(void)load{
[super load];
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// __NSArrayI
Method old = class_getInstanceMethod(objc_getClass("__NSArrayI"), @selector(objectAtIndex:));
Method new = class_getInstanceMethod(self, @selector(WG_objectAtIndexI:));
if (old && new) {
method_exchangeImplementations(old, new);
}
});
}
- (id)WG_objectAtIndexI:(NSUInteger)index {
if (index > self.count - 1 || !self.count) {
@try {
return [self WG_objectAtIndexI:index];
} @catch (NSException *exception) {
NSLog(@"不可数组越界了");
return nil;
} @finally {
}
} else {
return [self WG_objectAtIndexI:index];
}
}
正确交换方法如下:
+(void)load{
[super load];
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Method old = class_getInstanceMethod(objc_getClass("__NSArrayI"), @selector(objectAtIndex:));
Method new = class_getInstanceMethod(self, @selector(WG_objectAtIndex_NSArrayI:));
if (old && new) {
method_exchangeImplementations(old, new);
}
old = class_getInstanceMethod(objc_getClass("__NSSingleObjectArrayI"), @selector(objectAtIndex:));
new = class_getInstanceMethod(objc_getClass("__NSSingleObjectArrayI"), @selector(WG_objectAtIndex_NSSingleObjectArrayI:));
if (old && new) {
method_exchangeImplementations(old, new);
}
old = class_getInstanceMethod(objc_getClass("__NSArray0"), @selector(objectAtIndex:));
new = class_getInstanceMethod(objc_getClass("__NSArray0"), @selector(WG_objectAtIndex_NSArray0:));
if (old && new) {
method_exchangeImplementations(old, new);
}
});
}
- (id)WG_objectAtIndex_NSArrayI:(NSUInteger)index {
if (index > self.count - 1 || !self.count) {
@try {
return [self WG_objectAtIndex_NSArrayI:index];
} @catch (NSException *exception) {
NSLog(@"不可数组多元素越界了");
return nil;
} @finally {
}
} else {
return [self WG_objectAtIndex_NSArrayI:index];
}
}
- (id)WG_objectAtIndex_NSSingleObjectArrayI:(NSUInteger)index {
if (index > self.count - 1 || !self.count) {
@try {
return [self WG_objectAtIndex_NSSingleObjectArrayI:index];
} @catch (NSException *exception) {
NSLog(@"不可数组一个元素越界了");
return nil;
} @finally {
}
} else {
return [self WG_objectAtIndex_NSSingleObjectArrayI:index];
}
}
- (id)WG_objectAtIndex_NSArray0:(NSUInteger)index {
if (index > self.count - 1 || !self.count) {
@try {
return [self WG_objectAtIndex_NSArray0:index];
} @catch (NSException *exception) {
NSLog(@"不可数组0个元素越界了");
return nil;
} @finally {
}
} else {
return [self WG_objectAtIndex_NSArray0:index];
}
}
实验代码:
NSArray *arr1 = @[];
NSString *str1 = [arr1 objectAtIndex:5];
NSArray *arr2 = @[@"1"];
NSString *str2 = [arr2 objectAtIndex:5];
NSArray *arr3 = @[@"1", @"2", @"3"];
NSString *str3 = [arr3 objectAtIndex:5];
结果:不会崩溃了,打印日志如下
2018-04-20 16:47:16.717812+0800 CustomLearn[14152:1183376] 不可数组0个元素越界了
2018-04-20 16:47:16.718006+0800 CustomLearn[14152:1183376] 不可数组一个元素越界了
2018-04-20 16:47:16.718197+0800 CustomLearn[14152:1183376] 不可数组多元素越界了
网友评论