iOS常见代码错误和容错方法

  1. 数据类型安全篇
    • 数组操作
      1. + (instancetype)arrayWithObject:(id)anObject

此处参数应该判空,传入nil会引起崩溃

  1. – (id)objectAtIndex:(NSUInteger)index;

调用此方法前要先判断是否超过了数据的元素个数,否则会越界崩溃

  • – (NSArray *)arrayByAddingObject:(id)anObject;

调用此方式时传入参数应该判空,传入nil会引起崩溃

  1. NSMutableArray – (void)addObject:(id)anObject;

此处参数应该判空,传入nil会引起崩溃

  1. NSMutableArray – (void)insertObject:(id)anObject atIndex:(NSUInteger)index;

此处应该判断数据不为nil,且index不能越界

  1. NSMutableArray – (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject

同上,此处应该判断数据不为nil,且index不能越界

  • NSMutableArray – (void)removeObjectAtIndex:(NSUInteger)index;

此处需要判断index不能越界

  • NSDictionary操作
    1. + (instancetype)dictionaryWithObject:(id)object forKey:(id <NSCopying>)key;

此处需要判断key和object都不能为空,否则崩溃

  1. – (id)objectForKey:(id)aKey;

此处需要判断aKey不能为空;另外需要注意此方法可以KVC读取property的值,若没有propertyName,返回值为空;返回值不为空时需要判断类型为NSNull,或NSNumber,因为这两个类型也支持NSSecureCoding协议

– (void)setObject:(id)anObject forKey:(id <NSCopying>)aKey此处需要判断key和object都不能为空,否则崩溃

  • – (void)removeObjectForKey:(id)aKey;

此处需要判定aKey不为空,否则崩溃

  • NSNumber操作
    1. 模糊类型进行各种charValue,doubleValue,floatValue等等转换为基础类型的时候一定要先判断respondsToSelector:否则会因为找不到方法而闪退
  • NSString操作
    1. – (NSString *)substringFromIndex:(NSUInteger)from;

from值不能小于0或者大于string的长度,否则崩溃

  1. – (NSString *)substringToIndex:(NSUInteger)to

to值不能小于0或者大于string的长度,否则崩溃

  • – (NSString *)stringByReplacingCharactersInRange:(NSRange)range withString:(NSString *)replacement
    1. length == 0 api调用合法,但是就是一句费代码了,所以不要这样调用
    2. location > string.length api不合法,已经越界,会崩溃,所以需要判断
    3. location + range.length > string.length api不合法,已经越界,会崩溃,所以需要判断
  1. – (NSString *)stringByReplacingCharactersInRange:(NSRange)range withString:(NSString *)replacement
    1. length == 0 api调用合法,但是就是一句费代码了,所以不要这样调用
    2. location > string.length api不合法,已经越界,会崩溃,所以需要判断
    3. location + range.length > string.length api不合法,已经越界,会崩溃,所以需要判断
    4. replacement == nil 替换的string不能为空,会崩溃,所以如果是变量需要判断
  2. 各种doubleValue,integerValue转换时需要respondsToSelector:判断能否执行此方法,否则特殊情况下会出现崩溃

 

  1. 内存访问篇(原则:使用前判空,释放后置空)
    • 内存释放后访问变量,例:
      obj = nil;
      NSLog(obj);
    • ARC中内存过早释放,例如CLLocationManager,CMMotionManager,UIDocumentInteractionController等需要使用成员变量把变量指针持有,防止过早释放引起崩溃
    • 对不能保证非空的对象进行判空操作
      if(obj)
      { obj do sth.}
    • 堆栈溢出
      1. 主要存在在C和C++代码的死循环中分配内存,C和C++ 代码的循环操作要添加逻辑判断,避免死循环
      2. malloc 一定要记得free,new以后一定要delete
  • oc中无限死循环分配内存可能造成系统死机,所以更要避免
  • 内存泄露
    1. 主要存在于闭包和runloop的内存持有,闭包使用__weak或者__strong引用即可解决,RunLoop持有就停掉死循环的RunLoop
  1. 方法访问篇
    • 在delegate调用,performSelecter等不确定方法是否已经被实现的场景下先使用respondsToSelector:判断方法是否可调用
  2. 字节对其篇(概念可以百度资料)
    • 主要32位CPU和64位CPU字长差异引起崩溃,不写C和C++代码的可以无视,例:
char *mem = malloc(16); // alloc 16 bytes of data

double *dbl = mem + 2;

double set = 10.0;

*dbl = set; // 此处由于mem + 2造成字节不对其引起崩溃(double是4字节)

 

解决方案1:
double *dbl = mem + 4; // 正确的字节对其就好
解决方案2:
memcpy(dbl, &set, sizeof(set));// 使用内存拷贝,系统方法自动对其

 

 

  1. 线程时序篇
    • 当某个对象会被多个线程修改的时候,有可能一个线程访问这个对象的时候另一个线程已经把它删掉了,导致 Crash

解决方案:加锁
普通锁:NSLock 普通的锁,加锁的时候 lock,解锁调用 unlock。可以简化为@synchronized(obj) { [obj addObject:tempObj]; }

NSRecursiveLock 递归锁

使用普通的 NSLock 如果在递归的情况下或者重复加锁的情况下,自己跟自己抢资源导致死锁。oc 提供了 NSRecursiveLock 锁可以多次加锁而不会死锁,只要 unlock 次数跟 lock 次数一样就行了。
NSConditionLock 条件锁

多数情况下锁是不需要关心什么条件下 unlock 的,要用的时候锁上,用完了就 unlock 就完了。oc 提供这种条件锁,可以在满足某种条件下才解锁。这个锁的 lock 和 unlock, lockWhenCondition 是随意组合的,可以不用对应起来
不加锁,使用原子队列来解决http://coolshell.cn/articles/8239.html

  • 重复时钟

如果一个 Timer 是不停 repeat,那么释放之前就应该先 invalidate.因为NSTimer是通过Runloop来实现的,RunLoop会持有timer的强引用,如果在下一次repeat之前timer已经被释放掉了,RunLoop回调的时候就会找不到对象而崩溃

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注