iOS中常见的非野指针类crash总结

集合类Crash

数组越界

从数组中取某个index的元素时,先对index和数组元素个数进行判断:

1
2
3
if(index < self.array.count){
id obj = self.array[index];
}

注意:在使用数组时,要特别注意对边界条件的判断

向数组中插入空对象

向数组中插入数据时,先判断是否为nil

1
2
3
if(obj){
[array insertObject:obj atIndex:0];
}
调用可变字典setObject:ForKey方法,key或者value为空
  • 调用NSMutableDictionarysetValue:ForKey:方法而不是setObject:ForKey:方法,少用字面量语法。

    NSDictionary内部对value做了处理,[mutableDictionary setValue:nil ForKey:@"name"]不会崩溃。

  • set之前先对keyvalue进行非空判断,可以NSMutableArray以及NSMutableDictionary自定义一些安全的扩展方法,如:

1
2
3
4
5
6
7
8
9
10
11
12
13
-(id)objectAtIndexSafely:(NSUInteger)index {
if (index >= self.count) {
return nil;
}
return [self objectAtIndex:index];
}

-(void)setObjectSafely:(id)anObject forKey:(id <NSCopying>)aKey {
if (!aKey || !anObject) {
return;
}
[self setObject:anObject forKey:aKey];
}
一边遍历数组,一边修改数组内容,或者多线程环境中,一个线程在读另外一个线程在写,比如:
1
2
3
4
5
for(id item in self.itemArray){
if (item != self.currentItem) {
[self.itemArray removeItem:item];
}
}
解决方法:
  1. 遍历时需要修改原数组的时候可以遍历原数组的一个拷贝,如:保证多线程中读写操作的原子性:

    1
    2
    3
    4
    5
    6
    NSMutableArray *copyArray = [NSMutableArray arrayWithArray:self.items]; 
    for(id item in copyArray) {
    if (item != self.currentItem) {
    [self.items removeGuideViewItem:item];
    }
    }
  2. 方法:加锁,信号量,GCD串行队列,GCD dispatch_barrier_async方法等,dispatch_barrier_async用法

KVC类Crash

crash提示:
  • 调用setNilValueForKey抛出异常: Terminating app due to uncaught exception ‘NSInvalidArgumentException’, reason: ‘[ setNilValueForKey]: could not set nil as the value for the key age.’
  • 在类中找不到对应的key: Terminating app due to uncaught exception ‘NSUnknownKeyException’, reason: ‘[<ViewController 0x7ff968f700> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key undefined.’
解决方法:
  1. 重写setNilValueForKey方法

    1
    2
    3
    -(void)setNilValueForKey:(NSString *)key{ 
    NSLog(@"不能将%@设成nil",key);
    }
  2. 重写setValue:forUndefinedKey:方法

    1
    2
    -(void)setValue:(id)value forUndefinedKey:(NSString *)key{ 		NSLog(@"出现异常,该key不存在%@",key);
    }

KVO类Crash

KVO 对同一keypath多次removeObserver:
1
Terminating app due to uncaught exception 'NSRangeException', reason: 'Cannot remove an observer <UIView 0x7f9a90f0a0d0> for the key path "frame" from <ViewController 0x7f9a90e07010> because it is not registered as an observer.'
出现场景:

当对同一个keypath进行两次removeObserver时会导致程序crash,这种情况常常出现在父类有一个KVO,父类在dealloc中remove了一次,子类又remove了一次

解决方案:
  1. try&catch(容易掩盖问题)
  2. 确保addObserver和removeObserver一定要成对出现,推荐使用FaceBook开源的第三方库FBKVOController
  3. 可以分别在父类以及本类中定义各自的context字符串,比如在本类中定义context为@”myContext”;然后在dealloc中remove observer时指定移除的自身添加的observer。这样iOS就能知道移除的是自己的KVO,而不是父类中的KVO,避免二次remove造成crash。

找不到指定的方法

crash提示:

unrecognized selector sent to instance 0x1f7987d0'

解决方法:
  1. 头文件声明方法但是在.m文件中没有实现/把方法名修改但是没有在头文件中同步

  2. 调用代理类的方法的时候没有判断代理类是否已经实现对应的方法而直接调用,编译可以通过但是运行时会crash

  3. 对于id类型的对象没有判断类型直接强转调用方法

  4. @property (nonatomic, copy) NSMutableArray *mutableArray;用copy修饰的可变属性在赋值之后会变成不可变属性,比如这里调用addObject方法之后就会crash

  5. 在低版本的系统用了高版本才有的api 如:

    1
    2
    3
    if([str containsString:@"a"]){
    /*在iOS8以后才支持,iOS8以下调用会crash*/
    }