[iOS] 数组防止插入nil,取值越界造成崩溃

前一阵子遇到了数组插入空导致的崩溃问题, 由于时间问题没有去彻底解决它, 今天补上.
看了下网上的解决办法, 大致都是黑魔法, 或者继承,并重写父类方法, 总的看来不是方法不管用,而是覆盖的不够全面,导致有些情况下,分类的保护并没有卵用.
具体原因我也不一一赘述了. 好多文章可能都是转载, 自己并没有实际测试过, 简单的交换下那几个方法其实还是不够的, 废话不多说直接上代码.

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);

好了,就这么多, 谢谢支持

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容