php 数组 没有越界,iOS--再也不用担心数组越界

最近在网易云捕上看到一些数组越界导致的崩溃日志,所以决定数组的越界做一些处理。

fc3e1ff64299604a8386a6f4b393da85.png

崩溃报错信息

在项目的开发中,笔者一般遇到的问题就是,数组越界:-[__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 

return [self objectAtIndex:index];

}else{

return nil;

}

}

/**

*  防止数组越界

*/

- (id)objectAtIndexedSubscriptVerify:(NSUInteger)idx{

if (idx 

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 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]这样的取值到底调用了何种方法:

9e1c97412b9fda691e2ac140e6cb40ff.png

通过报错的函数,我们可以发现,数组索引调用的是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 

return [self jz_objectAtIndex:index];

}else{

NSLog(@" 你的NSMutableArray数组已经越界 帮你处理好了%ld   %ld   %@", index, self.count, [self class]);

return nil;

}

}

- (id)jz_objectAtIndexedSubscript:(NSUInteger)index{

if (index 

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 

return [self sxy_objectAtIndexedSubscript:idx];

}else{

NSLog(@" 你的 NSArray数组已经越界了 但是已经帮你处理好了  %ld   %ld", idx, self.count);

return nil;

}

}

- (id)sxy_objectAtIndex:(NSUInteger)index{

if (index 

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值