- 数据类型安全篇
- 数组操作
- + (instancetype)arrayWithObject:(id)anObject
此处参数应该判空,传入nil会引起崩溃
- – (id)objectAtIndex:(NSUInteger)index;
调用此方法前要先判断是否超过了数据的元素个数,否则会越界崩溃
- – (NSArray *)arrayByAddingObject:(id)anObject;
调用此方式时传入参数应该判空,传入nil会引起崩溃
- NSMutableArray – (void)addObject:(id)anObject;
此处参数应该判空,传入nil会引起崩溃
- NSMutableArray – (void)insertObject:(id)anObject atIndex:(NSUInteger)index;
此处应该判断数据不为nil,且index不能越界
- NSMutableArray – (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject
同上,此处应该判断数据不为nil,且index不能越界
- NSMutableArray – (void)removeObjectAtIndex:(NSUInteger)index;
此处需要判断index不能越界
- NSDictionary操作
- + (instancetype)dictionaryWithObject:(id)object forKey:(id <NSCopying>)key;
此处需要判断key和object都不能为空,否则崩溃
- – (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操作
- 模糊类型进行各种charValue,doubleValue,floatValue等等转换为基础类型的时候一定要先判断respondsToSelector:否则会因为找不到方法而闪退
- NSString操作
- – (NSString *)substringFromIndex:(NSUInteger)from;
from值不能小于0或者大于string的长度,否则崩溃
- – (NSString *)substringToIndex:(NSUInteger)to
to值不能小于0或者大于string的长度,否则崩溃
- – (NSString *)stringByReplacingCharactersInRange:(NSRange)range withString:(NSString *)replacement
- length == 0 api调用合法,但是就是一句费代码了,所以不要这样调用
- location > string.length api不合法,已经越界,会崩溃,所以需要判断
- location + range.length > string.length api不合法,已经越界,会崩溃,所以需要判断
- – (NSString *)stringByReplacingCharactersInRange:(NSRange)range withString:(NSString *)replacement
- length == 0 api调用合法,但是就是一句费代码了,所以不要这样调用
- location > string.length api不合法,已经越界,会崩溃,所以需要判断
- location + range.length > string.length api不合法,已经越界,会崩溃,所以需要判断
- replacement == nil 替换的string不能为空,会崩溃,所以如果是变量需要判断
- 各种doubleValue,integerValue转换时需要respondsToSelector:判断能否执行此方法,否则特殊情况下会出现崩溃
- 内存访问篇(原则:使用前判空,释放后置空)
- 内存释放后访问变量,例:
obj = nil;
NSLog(obj); - ARC中内存过早释放,例如CLLocationManager,CMMotionManager,UIDocumentInteractionController等需要使用成员变量把变量指针持有,防止过早释放引起崩溃
- 对不能保证非空的对象进行判空操作
if(obj)
{ obj do sth.} - 堆栈溢出
- 主要存在在C和C++代码的死循环中分配内存,C和C++ 代码的循环操作要添加逻辑判断,避免死循环
- malloc 一定要记得free,new以后一定要delete
- oc中无限死循环分配内存可能造成系统死机,所以更要避免
- 内存泄露
- 主要存在于闭包和runloop的内存持有,闭包使用__weak或者__strong引用即可解决,RunLoop持有就停掉死循环的RunLoop
- 方法访问篇
- 在delegate调用,performSelecter等不确定方法是否已经被实现的场景下先使用respondsToSelector:判断方法是否可调用
- 字节对其篇(概念可以百度资料)
- 主要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));// 使用内存拷贝,系统方法自动对其 |
- 线程时序篇
- 当某个对象会被多个线程修改的时候,有可能一个线程访问这个对象的时候另一个线程已经把它删掉了,导致 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回调的时候就会找不到对象而崩溃