[iOS]防止数组越界及添加空值的crash的扩展
2016-02-28 19:35
429 查看
本文主要是为了防止数组越界及添加到数组的值为nil的时候导致的程序crash,使用了两种方法来对NSArray进行扩展:
1. 替换系统方法
主要是对数组的如下两个方法进行的处理:
在说处理方式前先了解一下替换系统方法的runtime方法,具体实现原理可阅读本人转载的一边文章:[iOS]Objective-C Method Swizzling,文章中对实现替换的原理进行了讲解,这里就不再赘述;
使用了 runtime 中的方法,需要导入头文件:
这里,我对NSObject类进行了扩展NSObject+Until,加入了替换系统方法的一个类方法,其.h文件如下:
这个类主要是实现方法的替换,接下来是对NSArray和NSMutableArray的扩展
对于NSArray主要是替换系统的objectAtIndex:方法
在使用这些类的时候,系统会默认调用+(void)load方法,就是在此方法中实现方法的替换:
使用了dispatch_once_t,保证方法只替换一次即可;
objectAtIndex:是系统的方法,lqq_objectAtIndex:是自己定义的需要替换为的方法,其方法实现为:
注意这里的写法,在load方法中进行替换后,此处的[self lqq_objectAtIndex:]并不会行成递归,实际会执行[self
objectAtIndex:];
这样完成后,在使用时比较方便,只需正常的使用系统的objectAtIndex:方法,实际上会执行lqq_objectAtIndex:方法,在这个方法里进行一些判断就行;
对于NSMutableArray,其objectAtIndex:方法处理方法和上面一样,下面说下addObject:方法的处理,其+load方法实现如下:
将系统的addObject:替换为lqq_addObject:方法:
这样在使用时,只需直接使用系统的方法,在编译时会替换为自定义的方法;
测试如下:
输出结果如下:
2.自定义方法
这种实现起来比较简单,只需要扩展一个方法就行:
在使用的时候,就不能再使用系统的方法了,而是直接调用自定义的方法;
测试如下:
输出:
方法一,实现复杂,使用时方便,还是直接使用系统方法就行;方法二,实现简单,使用时需要直接调用自定义方法,不能使用系统方法;两种方法各有利弊,使用效果是一样的,自主选择即可!!
完整Demo下载
1. 替换系统方法
主要是对数组的如下两个方法进行的处理:
- (ObjectType)objectAtIndex:(NSUInteger)index; - (void)addObject:(ObjectType)object;
在说处理方式前先了解一下替换系统方法的runtime方法,具体实现原理可阅读本人转载的一边文章:[iOS]Objective-C Method Swizzling,文章中对实现替换的原理进行了讲解,这里就不再赘述;
使用了 runtime 中的方法,需要导入头文件:
#import <objc/runtime.h>
这里,我对NSObject类进行了扩展NSObject+Until,加入了替换系统方法的一个类方法,其.h文件如下:
#import <Foundation/Foundation.h> #import <objc/runtime.h> @interface NSObject (Until) /*! @method swizzleMethod:withMethod:error: @abstract 对系统方法进行替换 @param oldSelector 想要替换的方法 @param newSelector 实际替换为的方法 @param error 替换过程中出现的错误,如果没有错误为nil */ + (BOOL)swizzleMethod:(SEL)originalSelector withMethod:(SEL)swizzledSelector error:(NSError **)error; @end.m文件为:
#import "NSObject+Until.h" @implementation NSObject (Until) + (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
这个类主要是实现方法的替换,接下来是对NSArray和NSMutableArray的扩展
对于NSArray主要是替换系统的objectAtIndex:方法
在使用这些类的时候,系统会默认调用+(void)load方法,就是在此方法中实现方法的替换:
+(void)load { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ @autoreleasepool { [objc_getClass("__NSArrayI") swizzleMethod:@selector(objectAtIndex:) withMethod:@selector(lqq_objectAtIndex:) error:nil]; }; }); }
使用了dispatch_once_t,保证方法只替换一次即可;
objectAtIndex:是系统的方法,lqq_objectAtIndex:是自己定义的需要替换为的方法,其方法实现为:
- (id)lqq_objectAtIndex:(NSUInteger)index { if (index < self.count) { return [self lqq_objectAtIndex:index]; } return nil;//越界返回为nil }
注意这里的写法,在load方法中进行替换后,此处的[self lqq_objectAtIndex:]并不会行成递归,实际会执行[self
objectAtIndex:];
这样完成后,在使用时比较方便,只需正常的使用系统的objectAtIndex:方法,实际上会执行lqq_objectAtIndex:方法,在这个方法里进行一些判断就行;
对于NSMutableArray,其objectAtIndex:方法处理方法和上面一样,下面说下addObject:方法的处理,其+load方法实现如下:
+(void)load { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ @autoreleasepool { [objc_getClass("__NSArrayM") swizzleMethod: @selector(objectAtIndex:) withMethod:@selector(lqq_objectAtIndex:) error:nil]; [objc_getClass("__NSArrayM") swizzleMethod: @selector(addObject:) withMethod:@selector(lqq_addObject:) error:nil]; }; }); }
将系统的addObject:替换为lqq_addObject:方法:
-(void)lqq_addObject:(id)object { if (!object || [object isKindOfClass:[NSNull class]]) { [self lqq_addObject:@"kong"]; } else { [self lqq_addObject:object]; } }
这样在使用时,只需直接使用系统的方法,在编译时会替换为自定义的方法;
测试如下:
NSArray *array = @[@"a",@"d",@"f",@"g",@"t"]; //数组越界,但是不会crash for (int i = 0; i < 10; i++) { NSLog(@"array >>> %@",[array objectAtIndex:i]); } NSMutableArray *mArray = [[NSMutableArray alloc]initWithArray:array]; NSString *str = nil; //添加空值,不会crash [mArray addObject:str]; //可变数组越界,但是不会crash for (int i = 0; i < 10; i++) { NSLog(@"mArray >>> %@",[mArray objectAtIndex:i]); }
输出结果如下:
2016-02-28 19:26:12.060 SafetyArrayDemo[6540:470270] array >>> a 2016-02-28 19:26:12.061 SafetyArrayDemo[6540:470270] array >>> d 2016-02-28 19:26:12.061 SafetyArrayDemo[6540:470270] array >>> f 2016-02-28 19:26:12.062 SafetyArrayDemo[6540:470270] array >>> g 2016-02-28 19:26:12.062 SafetyArrayDemo[6540:470270] array >>> t 2016-02-28 19:26:12.063 SafetyArrayDemo[6540:470270] array >>> (null) 2016-02-28 19:26:12.063 SafetyArrayDemo[6540:470270] array >>> (null) 2016-02-28 19:26:12.063 SafetyArrayDemo[6540:470270] array >>> (null) 2016-02-28 19:26:12.063 SafetyArrayDemo[6540:470270] array >>> (null) 2016-02-28 19:26:12.064 SafetyArrayDemo[6540:470270] array >>> (null) 2016-02-28 19:26:12.064 SafetyArrayDemo[6540:470270] mArray >>> a 2016-02-28 19:26:12.064 SafetyArrayDemo[6540:470270] mArray >>> d 2016-02-28 19:26:12.065 SafetyArrayDemo[6540:470270] mArray >>> f 2016-02-28 19:26:12.066 SafetyArrayDemo[6540:470270] mArray >>> g 2016-02-28 19:26:12.066 SafetyArrayDemo[6540:470270] mArray >>> t 2016-02-28 19:26:12.067 SafetyArrayDemo[6540:470270] mArray >>> kong 2016-02-28 19:26:12.067 SafetyArrayDemo[6540:470270] mArray >>> (null) 2016-02-28 19:26:12.067 SafetyArrayDemo[6540:470270] mArray >>> (null) 2016-02-28 19:26:12.067 SafetyArrayDemo[6540:470270] mArray >>> (null) 2016-02-28 19:26:12.067 SafetyArrayDemo[6540:470270] mArray >>> (null)
2.自定义方法
这种实现起来比较简单,只需要扩展一个方法就行:
- (id)lqqNew_objectAtIndex:(NSUInteger)index{ // [super objectAtIndex:index]; if (index < self.count) { return self[index]; } else { return nil; } } -(void)lqqNew_addObject:(id)object { if (!object || [object isKindOfClass:[NSNull class]]) { [self addObject:@"kong"]; } else { [self addObject:object]; } }
在使用的时候,就不能再使用系统的方法了,而是直接调用自定义的方法;
测试如下:
NSArray *array = @[@"a",@"d",@"f",@"g",@"t"]; //数组越界,但是不会crash for (int i = 0; i < 10; i++) { NSLog(@"A222 >>> %@",[array lqqNew_objectAtIndex:i]); } NSMutableArray *mArray = [[NSMutableArray alloc]initWithArray:array]; NSString *str = nil; //添加空值,不会crash [mArray lqqNew_addObject:str]; //可变数组越界,但是不会crash for (int i = 0; i < 10; i++) { NSLog(@"mArray22 >>> %@",[mArray lqqNew_objectAtIndex:i]); }
输出:
2016-02-28 19:31:12.291 SafetyArrayDemo[6615:474083] A222 >>> a 2016-02-28 19:31:12.293 SafetyArrayDemo[6615:474083] A222 >>> d 2016-02-28 19:31:12.294 SafetyArrayDemo[6615:474083] A222 >>> f 2016-02-28 19:31:12.294 SafetyArrayDemo[6615:474083] A222 >>> g 2016-02-28 19:31:12.295 SafetyArrayDemo[6615:474083] A222 >>> t 2016-02-28 19:31:12.296 SafetyArrayDemo[6615:474083] A222 >>> (null) 2016-02-28 19:31:12.296 SafetyArrayDemo[6615:474083] A222 >>> (null) 2016-02-28 19:31:12.298 SafetyArrayDemo[6615:474083] A222 >>> (null) 2016-02-28 19:31:12.301 SafetyArrayDemo[6615:474083] A222 >>> (null) 2016-02-28 19:31:12.302 SafetyArrayDemo[6615:474083] A222 >>> (null) 2016-02-28 19:31:12.302 SafetyArrayDemo[6615:474083] mArray22 >>> a 2016-02-28 19:31:12.302 SafetyArrayDemo[6615:474083] mArray22 >>> d 2016-02-28 19:31:12.303 SafetyArrayDemo[6615:474083] mArray22 >>> f 2016-02-28 19:31:12.303 SafetyArrayDemo[6615:474083] mArray22 >>> g 2016-02-28 19:31:12.303 SafetyArrayDemo[6615:474083] mArray22 >>> t 2016-02-28 19:31:12.303 SafetyArrayDemo[6615:474083] mArray22 >>> kong 2016-02-28 19:31:12.304 SafetyArrayDemo[6615:474083] mArray22 >>> (null) 2016-02-28 19:31:12.333 SafetyArrayDemo[6615:474083] mArray22 >>> (null) 2016-02-28 19:31:12.334 SafetyArrayDemo[6615:474083] mArray22 >>> (null) 2016-02-28 19:31:12.335 SafetyArrayDemo[6615:474083] mArray22 >>> (null)
方法一,实现复杂,使用时方便,还是直接使用系统方法就行;方法二,实现简单,使用时需要直接调用自定义方法,不能使用系统方法;两种方法各有利弊,使用效果是一样的,自主选择即可!!
完整Demo下载
相关文章推荐
- iOS实践01
- 文章标题
- iOS进阶之旅-可交互式转场动画
- iOS_SN_沙盒文件操作及位置
- iOS模态视图从N级退回到第一级方法
- iOS_SN_详解沙河(转载)
- iOS中 本地通知/本地通知详解 韩俊强的博客
- iOS中 本地通知/本地通知详解 韩俊强的博客
- iOS_SN_push/pop转场动画封装和一般动画封装
- iOS 保存图片到相册
- iOS9 的白名单问题
- ios 第三方qq授权登陆,第一次登陆后,再次登陆,失效
- iOS-KVC/KVO学习
- iOS-KVO
- 适配ios9遇到的坑
- NSDate与NSDateFormatter
- iOS_SN_深浅拷贝( 百度的)_转载
- iOS开发------runtime之动态添加方法(动态决议,请求转发)
- iOS自定义AlertView
- IOS开发日志之Xcode一些好用的插件大全