内容简介:最近在网易云捕上看到一些数组越界导致的崩溃日志,所以决定数组的越界做一些处理。崩溃报错信息
最近在网易云捕上看到一些数组越界导致的崩溃日志,所以决定数组的越界做一些处理。
崩溃报错信息
在项目的开发中,笔者一般遇到的问题就是,数组越界:
-[__NSArrayM objectAtIndex:]: index 0 beyond bounds for empty array; -[__NSArrayM objectAtIndexedSubscript:]: index 0 beyond bounds for empty array;
很明显,这两个函数是在数组取值的时候发生的越界情况,在网上搜索了很多大神的文章,也自己总结了一下,下面罗列出两种处理方法:
一、为NSArray、NSMutableArray添加分类并添加方法
首先,我们为NSarray创建分类并添加方法,在.h文件中:
@interface NSArray (ErrorHandle) /** 为数组分类添加的方法 可以在应用中直接调用 可以防止数组越界导致的crash @param index 传入的取值下标 @return id类型的数据 */ - (id)objectAtIndexVerify:(NSUInteger)index; - (id)objectAtIndexedSubscriptVerify:(NSUInteger)idx; @end
在.m文件中,我们可以将这两个方法实现出来:
@implementation NSArray (ErrorHandle) /** * 防止数组越界 */ - (id)objectAtIndexVerify:(NSUInteger)index{ if (index < self.count) { return [self objectAtIndex:index]; }else{ return nil; } } /** * 防止数组越界 */ - (id)objectAtIndexedSubscriptVerify:(NSUInteger)idx{ if (idx < self.count) { return [self objectAtIndexedSubscript:idx]; }else{ return nil; } }
类似的,我们可以为NSMutableArray创建分类,(在可变数组中,我们插入nil对象也会产生crash,所以我们要对可变数组做特殊处理)
#import
@interface NSMutableArray (ErrorHandle)
/**
数组中插入数据
@param object 数据
@param index 下标
*/
- (void)insertObjectVerify:(id)object atIndex:(NSInteger)index;
/**
数组中添加数据
@param object 数据
*/
- (void)addObjectVerify:(id)object;
@end
在可变数组的.m文件中
@implementation NSMutableArray (ErrorHandle) /** * 数组中插入数据 */ - (void)insertObjectVerify:(id)object atIndex:(NSInteger)index{ if (index < self.count && object) { [self insertObject:object atIndex:index]; } } /** * 数组中添加数据 */ - (void)addObjectVerify:(id)object{ if (object) { [self addObject:object]; } }
特别说明:以上方法在项目的实际运用中,要想防止数组越界,就需要调用我们自己添加的方法了,而不要调用系统的了。
二、用runtime处理数组越界
不到万不得已,笔者一般是不想用runtime的。不过runtime确确实实也能解决数组越界的问题,在我们数组越界处理的第一种方法中,我们可以看见,我们无法使用索引来从数组中取值了(即类似:cell.textLabel.text = self.dataSource[indexPath.row];这样的取值方式)。那如果我们想要这种取值方式的话,就需要用runtime来实现了。
首先,我们先来确定下self.dataSource[indexPath.row]这样的取值到底调用了何种方法:
通过报错的函数,我们可以发现,数组索引调用的是objectAtIndexedSubscript:这个函数。找到了报错的函数,我们就可以通过runtime来实现函数的交换。首先,我们为NSObject写一个分类,方便我们调用交换系统和自定义的方法:
#import
@interface NSObject (SwizzleMethod)
/**
* 对系统方法进行替换(交换实例方法)
*
* @param systemSelector 被替换的方法
* @param swizzledSelector 实际使用的方法
* @param error 替换过程中出现的错误消息
*
* @return 是否替换成功
*/
+ (BOOL)SystemSelector:(SEL)systemSelector swizzledSelector:(SEL)swizzledSelector error:(NSError *)error;
@end
在.m文件中,我们需要将方法实现出来:
#import "NSObject+SwizzleMethod.h"
#import
@implementation NSObject (SwizzleMethod)
/**
* 对系统方法进行替换
*
* @param systemSelector 被替换的方法
* @param swizzledSelector 实际使用的方法
* @param error 替换过程中出现的错误消息
*
* @return 是否替换成功
*/
+ (BOOL)SystemSelector:(SEL)systemSelector swizzledSelector:(SEL)swizzledSelector error:(NSError *)error{
Method systemMethod = class_getInstanceMethod(self, systemSelector);
if (!systemMethod) {
return NO;
}
Method swizzledMethod = class_getInstanceMethod(self, swizzledSelector);
if (!swizzledMethod) {
return NO;
}
if (class_addMethod([self class], systemSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))) {
class_replaceMethod([self class], swizzledSelector, method_getImplementation(systemMethod), method_getTypeEncoding(systemMethod));
} else{
method_exchangeImplementations(systemMethod, swizzledMethod);
}
return YES;
}
@end
在方法交换和替换的过程中,如果被替换的方法或者我们将要使用的方法没有的话,直接ruturn,不进行方法互换,经过双重检验才进行方法的互换。
我们以NSMutableArray为例子,同样的NSMutableArray添加分类,在.h文件中只需要写下如下代码:
+(void)load{ [super load]; //无论怎样 都要保证方法只交换一次 static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ //交换NSMutableArray中的方法 [objc_getClass("__NSArrayM") SystemSelector:@selector(objectAtIndex:) swizzledSelector:@selector(jz_objectAtIndex:) error:nil]; //交换NSMutableArray中的方法 [objc_getClass("__NSArrayM") SystemSelector:@selector(objectAtIndexedSubscript:) swizzledSelector:@selector(jz_objectAtIndexedSubscript:) error:nil]; }); } - (id)jz_objectAtIndex:(NSUInteger)index{ if (index < self.count) { return [self jz_objectAtIndex:index]; }else{ NSLog(@" 你的NSMutableArray数组已经越界 帮你处理好了%ld %ld %@", index, self.count, [self class]); return nil; } } - (id)jz_objectAtIndexedSubscript:(NSUInteger)index{ if (index < self.count) { return [self jz_objectAtIndexedSubscript:index]; }else{ NSLog(@" 你的NSMutableArray数组已经越界 帮你处理好了%ld %ld %@", index, self.count, [self class]); return nil; } }
同样的,我们也可以在NSArray的分类中添加如下代码:
+(void)load{ [super load]; //无论怎样 都要保证方法只交换一次 static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ //交换NSArray中的objectAtIndex方法 [objc_getClass("__NSArrayI") SystemSelector:@selector(objectAtIndex:) swizzledSelector:@selector(sxy_objectAtIndex:) error:nil]; //交换NSArray中的objectAtIndexedSubscript方法 [objc_getClass("__NSArrayI") SystemSelector:@selector(objectAtIndexedSubscript:) swizzledSelector:@selector(sxy_objectAtIndexedSubscript:) error:nil]; }); } - (id)sxy_objectAtIndexedSubscript:(NSUInteger)idx{ if (idx < self.count) { return [self sxy_objectAtIndexedSubscript:idx]; }else{ NSLog(@" 你的 NSArray数组已经越界了 但是已经帮你处理好了 %ld %ld", idx, self.count); return nil; } } - (id)sxy_objectAtIndex:(NSUInteger)index{ if (index < self.count) { return [self sxy_objectAtIndex:index]; }else{ NSLog(@" 你的 NSArray数组已经越界了 但是已经帮你处理好了 %ld %ld", index, self.count); return nil; } }
关于上面的Demo,笔者已经上传git,需要的小伙伴去下载吧! 数组越界Demo
总结: 以上两种方法目前用的都可行,貌似用runtime封装虽然复杂一点,但是使用起来更为隐蔽,也更自如一些,并且之前的数组取值不用做改动。大家在项目中两种方法,可以喜欢哪种用哪种了,妈妈再也不用担心我的数组越界了!!!(此处只是添加了数组取值时候的防止越界,在实际项目中可能用到的不止这几种方法(例如插入),大家可以根据自己的实际需要添加)。如果Demo和文章中有理解不到位或者不足的地方,请大家多多指教,蟹蟹,也祝大家新年快乐!!
作者:橘子star
链接:https://www.jianshu.com/p/1f5c3d43b587
以上所述就是小编给大家介绍的《iOS--再也不用担心数组越界》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 从pwnable.tw-calc看数组越界造成的任意地址读写
- 【缺陷周话】第5期 :越界访问
- 《缺陷周话》第5期:越界访问
- v8利用入门:从越界访问到RCE
- v8利用入门-从越界访问到rce
- golang uint8转int8越界
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
产品的视角:从热闹到门道
后显慧 / 机械工业出版社 / 2016-1-1 / 69.00
本书在创造性的提出互联网产品定义的基础上,为读者提供了一个从0基础到产品操盘手的产品思维培养方法! 全书以互联网产品定义为基础,提出了产品思维学习的RAC模型,通过认识产品、还原产品和创造产品三个阶段去培养产品思维和产品认知。 通过大量的图片和视觉引导的方法,作者像零基础的用户深入浅出的描绘了一条产品经理的自我修养路径,并且提供了知识地图(knowledge map)和阅读雷达等工具,......一起来看看 《产品的视角:从热闹到门道》 这本书的介绍吧!