前一阵子遇到了数组插入空导致的崩溃问题, 由于时间问题没有去彻底解决它, 今天补上.
看了下网上的解决办法, 大致都是黑魔法, 或者继承,并重写父类方法, 总的看来不是方法不管用,而是覆盖的不够全面,导致有些情况下,分类的保护并没有卵用.
具体原因我也不一一赘述了. 好多文章可能都是转载, 自己并没有实际测试过, 简单的交换下那几个方法其实还是不够的, 废话不多说直接上代码.
Demo Git 地址 https://github.com/wanyiyuan/NSArraySaveDemo.git
我用的也是分类
NSObject 分类
//.h
#import <Foundation/Foundation.h>
@interface NSObject (Runtime)
#pragma mark - 方法交换
- (void)swizzleMethod:(SEL)originalSelector swizzledSelector:(SEL)swizzledSelector;
@end
//.m
#import "NSObject+Runtime.h"
#import <objc/runtime.h>
#import <UIKit/UIKit.h>
@implementation NSObject (Runtime)
/**
键盘弹起,程序进入后台后,会崩溃,原因是app进入后台后,系统会调用数组分类中的方法,
导致访问了未识别的方法崩溃,暂时的解决手段是:让键盘退出第一响应者,关于这个bug
可以参考下网上的处理办法,比如对应的类加入MRC环境,但是由于我写的这套里边有将nil
变成NSNull来处理, 所以会出现改成MRC环境后,会出现一个访问未识别方法的崩溃信息,
如果想用这套逻辑的话,我的做法就是暂时先退出键盘的响应者, 如果你舍弃将nil变成NSNull
插入到数组中的话,可以将涉及到的类变成MRC环境
*/
+(void)applicationWillResignActiveNotification:(NSNotification *)note{
[[[UIApplication sharedApplication] keyWindow] endEditing:YES];
//[[UIApplication sharedApplication] sendAction:@selector(resignFirstResponder) to:nil from:nil forEvent:nil];
}
+(void)load{
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillResignActiveNotification:) name:UIApplicationWillResignActiveNotification object:nil];
}
#pragma mark - 方法交换
- (void)swizzleMethod:(SEL)originalSelector swizzledSelector:(SEL)swizzledSelector{
Class class = [self class];
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
BOOL didAddMethod = class_addMethod(class,
originalSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
if (didAddMethod) {
class_replaceMethod(class,
swizzledSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
}
@end
NSArray分类
//.h
#import <Foundation/Foundation.h>
@interface NSArray (Save)
@end
//.m
#import "NSArray+Save.h"
#import <objc/runtime.h>
#import "NSObject+Runtime.m"
@implementation NSArray (Save)
+ (void)load{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
@autoreleasepool {
/** 不可变数组 */
//空
[objc_getClass("__NSArray0") swizzleMethod:@selector(objectAtIndex:) swizzledSelector:@selector(emptyObjectAtIndex:)];
//非空
[objc_getClass("__NSArrayI") swizzleMethod:@selector(objectAtIndex:) swizzledSelector:@selector(anyObjectAtIndex:)];
/** 可变数组 */
[objc_getClass("__NSArrayM") swizzleMethod:@selector(objectAtIndex:) swizzledSelector:@selector(mutableObjectAtIndex:)];
[objc_getClass("__NSArrayM") swizzleMethod:@selector(insertObject:atIndex:) swizzledSelector:@selector(mutableInsertObject:atIndex:)];
[objc_getClass("__NSArrayM") swizzleMethod:@selector(addObject:) swizzledSelector:@selector(mutableAddObject:)];
/** 只有一个元素 */
//数组中只有一个元素
[objc_getClass("__NSSingleObjectArrayI") swizzleMethod:@selector(objectAtIndex:) swizzledSelector:@selector(singleObjectIndex:)];
[objc_getClass("__NSSingleObjectArrayI") swizzleMethod:@selector(insertObject:atIndex:) swizzledSelector:@selector(singleInsertObject:atIndex:)];
[objc_getClass("__NSSingleObjectArrayI") swizzleMethod:@selector(addObject:) swizzledSelector:@selector(singleAddObject:)];
/** 类方法创建的数组,插入空时,下面这两个会崩溃 */
[objc_getClass("__NSPlaceholderArray") swizzleMethod:@selector(initWithObjects:count:) swizzledSelector:@selector(swizzInitWithObjects:count:)];
}
});
}
#pragma mark - 不可变
/**
空:nil 或 count = 0
*/
- (id)emptyObjectAtIndex:(NSInteger)index{
NSLog(@"数组 = nil 或者 count = 0 返回 nil %s",__FUNCTION__);
return nil;
}
/**
多个元素
*/
- (id)anyObjectAtIndex:(NSInteger)index{
if (index >= self.count || index < 0) {
NSLog(@"取值时: 索引越界,返回 nil %s",__FUNCTION__);
return nil;
}
id obj = [self anyObjectAtIndex:index];
if ([obj isKindOfClass:[NSNull class]]) {
NSLog(@"取值时: 取出的元素类型为 NSNull 类型,返回 nil %s",__FUNCTION__);
return nil;
}
return obj;
}
#pragma mark - 两个类方法引起的崩溃
/**
arrayWithObject
arrayWithObjects:nil count:1
*/
-(id)swizzInitWithObjects:(const id [])objects count:(NSUInteger)cnt{
for (int i=0; i<cnt; i++) {
if (objects == NULL){
NSLog(@"objects 为 NULL, 返回 nil %s",__FUNCTION__);
return nil;
}
if (objects[i] == nil){
NSLog(@"取值时: 取出的元素为 nil, 返回 nil %s",__FUNCTION__);
return nil;
}
}
return [self swizzInitWithObjects:objects count:cnt];
}
#pragma mark - 可变数组
/**
取值
*/
- (id)mutableObjectAtIndex:(NSInteger)index{
if (index >= self.count || index < 0) {
NSLog(@"取值时: 索引越界, 返回 nil %s",__FUNCTION__);
return nil;
}
id obj = [self mutableObjectAtIndex:index];
if ([obj isKindOfClass:[NSNull class]]) {
NSLog(@"取值时: 取出的元素类型为 NSNull, 返回 nil %s",__FUNCTION__);
return nil;
}
return obj;
}
/**
插入
*/
- (void)mutableInsertObject:(id)object atIndex:(NSUInteger)index{
if (object) {
[self mutableInsertObject:object atIndex:index];
}else{
NSLog(@"插入值时: 元素类型为 nil, %s",__FUNCTION__);
[self mutableInsertObject:[NSNull null] atIndex:index];
}
}
-(void)mutableAddObject:(id)object{
if (object) {
[self mutableAddObject:object];
}else{
NSLog(@"插入值时: 元素类型为 nil, %s",__FUNCTION__);
[self mutableAddObject:[NSNull null]];
}
}
#pragma mark - 数组中只有一个元素时三个方法不分可变和不可变
/**
取值
*/
- (id)singleObjectIndex:(NSInteger)index{
if (index >= self.count || index < 0) {
NSLog(@"数组中只有一个元素, 取值时: 索引越界, 返回 nil %s",__FUNCTION__);
return nil;
}
id obj = [self singleObjectIndex:index];
if ([obj isKindOfClass:[NSNull class]]) {
NSLog(@"数组中只有一个元素, 取值时: 元素类型为 NSNull 类型, 返回 nil %s",__FUNCTION__);
return nil;
}
return obj;
}
/**
插入
*/
- (void)singleInsertObject:(id)object atIndex:(NSUInteger)index{
if (object) {
[self singleInsertObject:object atIndex:index];
}else{
//数组中有一个元素时,判断下真实类型,如果是NSArray,则不添加
Class superClass = self.superclass;
NSString *superClassStr = NSStringFromClass(superClass);
if (![superClassStr isEqualToString:@"NSArray"]) {
NSLog(@"数组中只有一个元素, 并且数组真实类型为NSMutableArray 插入值: 元素类型为 nil, %s",__FUNCTION__);
[self singleInsertObject:[NSNull null] atIndex:index];
}else{
NSLog(@"真实类型是NSArray,什么都不做 %s",__FUNCTION__);
}
}
}
-(void)singleAddObject:(id)object{
if (object) {
[self singleAddObject:object];
}else{
//数组中有一个元素时,判断下真实类型,如果是NSArray,则不添加
Class superClass = self.superclass;
NSString *superClassStr = NSStringFromClass(superClass);
if (![superClassStr isEqualToString:@"NSArray"]) {
NSLog(@"数组中只有一个元素, 并且数组真实类型为NSMutableArray 插入值: 元素类型为 nil, %s",__FUNCTION__);
[self singleAddObject:[NSNull null]];
}else{
NSLog(@"真实类型是NSArray,什么都不做 %s",__FUNCTION__);
}
}
}
@end
测试代码,如果你想测试的话,自己简单的创建个文中用到的Person类
/** 不可变数组 */
//角标越界
//数组 = nil
NSArray *array = nil;
NSLog(@"array[1] = %@",array[2]);
//count = 0
NSArray *array1 = @[];
NSLog(@"array1[1] = %@",array1[2]);
//只有一个元素
NSArray *array2 = @[@"123"];
NSLog(@"array2[2] = %@",array2[2]);
//多个元素
NSArray *array3 = @[@"123",@"321",@"456"];
NSLog(@"array3[8] = %@",array3[8]);
//创建数组传入nil
NSArray *array4 = [NSArray arrayWithContentsOfURL:nil];
NSArray *array5 = [NSArray arrayWithObjects:nil, nil];
NSArray *array6 = [NSArray arrayWithArray:nil];
NSArray *array7 = [NSArray arrayWithObject:nil];
NSArray *array8 = [NSArray arrayWithObjects:nil count:1];
/**
* 可变指针 指向 不可变数组
* 此种指向需要注意,如果用不可变数组对象,访问了可变数组的方法会造成崩溃
* 下面的数组其真实类型是不可变数组
*/
NSMutableArray *arrayI = nil;
NSLog(@"arrayI[1] = %@",arrayI[2]);
[arrayI addObject:nil];
[arrayI insertObject:nil atIndex:0];
//count = 0
NSMutableArray *array1I = @[];
NSLog(@"array1I[1] = %@",array1I[2]);
//[array1I addObject:nil];//语法错误,不可变访问可变方法,崩溃
//[array1I insertObject:nil atIndex:0];//语法错误,不可变访问可变方法,崩溃
//只有一个元素
NSMutableArray *array2I = @[@"123"];
NSLog(@"array2I[2] = %@",array2I[2]);
//多个元素
NSMutableArray *array3I = @[@"123",@"321",@"456"];
NSLog(@"array3I[8] = %@",array3I[8]);
//创建数组传入nil
NSMutableArray *array4I = [NSArray arrayWithContentsOfURL:nil];
NSMutableArray *array5I = [NSArray arrayWithObjects:nil, nil];
NSMutableArray *array6I = [NSArray arrayWithArray:nil];
NSMutableArray *array7I = [NSArray arrayWithObject:nil];
NSMutableArray *array8I = [NSArray arrayWithObjects:nil count:1];
/**
* 可变指针 指向 可变数组
*/
NSMutableArray *arrayM = nil;
[arrayM addObject:nil];
[arrayM insertObject:nil atIndex:3];
NSMutableArray *array1M = [NSMutableArray array];
[array1M addObject:nil];
[array1M insertObject:nil atIndex:0];
NSMutableArray *array2M = [[NSMutableArray alloc] init];
[array2M addObject:nil];
[array2M insertObject:nil atIndex:1];
NSMutableArray *array3M = [NSMutableArray arrayWithObject:@"123"];
[array3M addObject:nil];
[array3M insertObject:nil atIndex:1];
NSMutableArray *array16M = [NSMutableArray arrayWithContentsOfURL:nil];
NSMutableArray *array18M = [NSMutableArray arrayWithObjects:nil, nil];
NSMutableArray *array19M = [NSMutableArray arrayWithArray:nil];
NSMutableArray *array14M = [NSMutableArray arrayWithObject:nil];
NSMutableArray *array15M = [NSMutableArray arrayWithObjects:nil count:1];
//测试插入对象为空
NSMutableArray *array20M = [NSMutableArray array];
[array20M addObject:nil];
[array20M addObject:[[Person alloc]init]];
[array20M addObject:[[Person alloc]init]];
NSLog(@"array16M = %@",array20M);
id obj = array20M[0];
if ([obj isKindOfClass:[NSNull class]]) {
NSLog(@"我是个null");
}
Person *p1 = array20M[0];
NSLog(@"p1.name = %@",p1.name);
好了,就这么多, 谢谢支持