一、什么是数组越界
数组越界,很多新手容易中招的bug,
NSArray *ar = @[@"123", @(456), @"haha"];
nsstring *str = ar[3];//数组取值越界了
这个看起来很简单,但是实际开发应用中,各种数据变量在程序运行过程中,是经常不断变化的,很容易被忽视,一不小心就越界导致crash。我们先不论为什么越界,这是另一个议题,至少最基本的我们要先保证程序不会奔溃是吧。
二、数组越界最简单的一种解决方案,当然就是取值的时候限制下index的值,比如:
NSArray *moduleTexts = @[@"资讯系统", @"数字资源", @"震例系统", @"百科系统"];
int index = self.moduleCode.intValue-1;//moduleCode是服务器返回值,NSString类型
index = MAX(MIN(index, 3), 0);
lb_moduleType.text = moduleTexts[index];
三、在数组的分类中使用runtime机制来交换方法
基本思路是:当数组越界时返回nil,没有越界时返回原本的index.这样就能达到防止程序崩溃的问题.
1、创建NSObject的Category分类
此分类文件就一个方法,该方法用runtime的机制来替换系统方法。
NSObject+ExchangeMethod.h文件:
#import <Foundation/Foundation.h>
@interface NSObject (ExchangeMethod)
/**
* 对系统方法进行替换(交换实例方法)
*
* @param systemSelector 被替换的方法
* @param changedSelector 实际使用的方法
* @param error 替换过程中出现的错误消息
*
* @return 是否替换成功
*/
+ (BOOL)exchangedSystemSelector:(SEL)systemSelector withSelector:(SEL)changedSelector error:(NSError *)error;
@end
创建.m文件NSObject+ExchangeMethod.m:
#import "NSObject+ExchangeMethod.h"
#import <objc/runtime.h>
@implementation NSObject (ExchangeMethod)
+ (BOOL)exchangedSystemSelector:(SEL)systemSelector withSelector:(SEL)changedSelector error:(NSError *)error{
Method systemMethod = class_getInstanceMethod(self, systemSelector);
if (!systemMethod) {
return NO;
}
Method changedMethod = class_getInstanceMethod(self, changedSelector);
if (!changedMethod) {
return NO;
}
if (class_addMethod([self class], systemSelector, method_getImplementation(changedMethod), method_getTypeEncoding(changedMethod))) {
class_replaceMethod([self class], changedSelector, method_getImplementation(systemMethod), method_getTypeEncoding(systemMethod));
}else{
method_exchangeImplementations(systemMethod, changedMethod);
}
return YES;
}
@end
2、创建NSArray的分类,
NSArray+Overflow.h文件:
#import <objc/runtime.h>
#import <Foundation/Foundation.h>
@interface NSArray (Overflow)
@end
NSArray+Overflow.m文件:
#import "NSArray+Overflow.h"
#import "NSObject+ExchangeMethod.h"
#import <objc/runtime.h>
@implementation NSArray (Overflow)
+(void)load{
[super load];
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[objc_getClass("__NSArrayI") exchangedSystemSelector:@selector(objectAtIndex:) withSelector:@selector(hd_objectAtIndex:) error:nil];
[objc_getClass("__NSArrayI") exchangedSystemSelector:@selector(objectAtIndexedSubscript:) withSelector:@selector(hd_objectAtIndexedSubscript:) error:nil];
});
}
- (id)hd_objectAtIndexedSubscript:(NSUInteger)index{
if (index < self.count) {
return [self hd_objectAtIndexedSubscript:index];
}else{
NSLog(@"Error:数组越界了,index = %ld, count = %ld", index, self.count);
return nil;
}
}
- (id)hd_objectAtIndex:(NSUInteger)index{
if (index < self.count) {
return [self hd_objectAtIndex:index];
}else{
NSLog(@"Error:数组越界了,index = %ld, count = %ld", index, self.count);
return nil;
}
}
@end
3、创建NSMutabeArray的分类,
NSMutableArray+Overflow.h文件:
#import <Foundation/Foundation.h>
@interface NSMutableArray (Overflow)
@end
NSMutableArray+Overflow.m文件:
#import "NSMutableArray+Overflow.h"
#import "NSObject+ExchangeMethod.h"
@implementation NSMutableArray (Overflow)
+(void)load{
[super load];
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[objc_getClass("__NSArrayM") exchangedSystemSelector:@selector(objectAtIndex:) withSelector:@selector(hd_objectAtIndex:) error:nil];
[objc_getClass("__NSArrayM") exchangedSystemSelector:@selector(objectAtIndexedSubscript:) withSelector:@selector(hd_objectAtIndexedSubscript:) error:nil];
});
}
- (id)hd_objectAtIndexedSubscript:(NSUInteger)idx{
if (idx < self.count) {
return [self hd_objectAtIndexedSubscript:idx];
}else{
NSLog(@"Error:数组越界了,index = %ld, count = %ld", idx, self.count);
return nil;
}
}
- (id)hd_objectAtIndex:(NSUInteger)index{
if (index < self.count) {
return [self hd_objectAtIndex:index];
}else{
NSLog(@"Error:数组越界了,index = %ld, count = %ld", index, self.count);
return nil;
}
}
@end
4、最后在项目工程下XXXX-Prefix.pch文件里添加NSArray和NSMutableAray的分类头文件就行了:
#ifdef __OBJC__
#define MAS_SHORTHAND
#import "NSArray+overflow.h"
#import "NSMutableArray+Overflow.h"
#endif