这篇文章列出了9种常见的crash,原文写得很好,我这里对照我本身遇到过的状况再整理记录下。javascript
KVO的一种经常使用场景是view对象监听view model对象实现实时刷新UI,例若有一个table view,每一个cell都监听对应的cell model,这样数据源数组中只有一个对象的属性发生改变时就不须要reload整个列表。java
使用KVO有一个常见的crash就是没有移除监听,咱们须要在dealloc方法中执行removeObserver方法。这里推荐facebook开源的KVOController,让咱们更方便地使用KVO。git
咱们常常会遇到集合遍历的crash,有一点须要注意,在遍历可变集合(NSMutableArray,NSMutableDictionary,NSMutableSet)时,不可以对集合作修改,例如增长或删除集合中的元素。这个问题最好是从代码规范上避免,例如接口中不该该暴露可变集合,而是暴露readonly的集合。如下是推荐的一种写法:github
People.h编程
#import <Foundation/Foundation.h>
@interface People : NSObject
@property (nonatomic, strong, readonly) NSArray *friends;
- (void)addFriend:(id)aFriend;
- (void)removeFriend:(id)aFriend;
@end复制代码
People.m数组
#import "People.h"
@interface People ()
@property (nonatomic, strong) NSMutableArray *internalFriends;
@end
@implementation People
- (void)dealloc
{
//
}
- (instancetype)init
{
self = [super init];
if (self) {
_internalFriends = [NSMutableArray new];
}
return self;
}
- (void)addFriend:(id)aFriend
{
if (aFriend == nil) {
return;
}
@synchronized(self)
{
[_internalFriends addObject:aFriend];
}
}
- (void)removeFriend:(id)aFriend
{
if (aFriend == nil) {
return;
}
@synchronized(self)
{
[_internalFriends removeObject:aFriend];
}
}
//NSMutableArray copy -> NSArray
- (NSArray *)friends
{
return [_internalFriends copy];
}
@end复制代码
还有一点要注意的是,对于第三方接口返回的集合,咱们都要怀疑其正确性,有可能接口中写明是不可变的可是实际返回的是可变集合,若是咱们直接按照不可变来使用就有可能触发crash,所以在集合遍历前先对第三方接口返回的数据作一次copy操做是一个好的习惯。多线程
NSNotification是一种一对多的监听机制,有一种常见的crash是对象dealloc后没有移除监听。async
咱们能够根据具体的通知名称移除,例如性能
[[NSNotificationCenter defaultCenter] removeObserver:self name:kSomeNotificationName object:someObject];
[[NSNotificationCenter defaultCenter] removeObserver:self name:kSomeOtherNotificationName object:someOtherObject];
etc...复制代码
上述方法没有问题,可是不利于维护,好比后期又有需求须要添加新的通知来实现,对应的就须要添加代码来移除,要是一不当心忘记移除就会触发crash,更加推荐的方式是在dealloc中使用
[[NSNotificationCenter defaultCenter] removeObserver:self];
来移除测试
在注册监听通知时有一个问题须要注意,经测试,重复注册会致使回调方法进入屡次,注册几回,回调就会进入几回。咱们常常在viewDidLoad中注册监听,可是view是有可能unloaded再reloaded的,所以viewDidLoad就有可能执行屡次致使重复注册。
对于一个对象,它的init方法只会执行一次,dealloc方法也是,所以在这两个方法中执行注册和移除就能保证注册和移除是平衡的,下降了问题排查的难度。
[NSNotificationCenter addObserverForName:object:queue:usingBlock:]
提供了block的方法来使用通知,可是咱们应该避免使用这种方式,由于这须要咱们在后续代码里单独移除,这就增长了出错的可能,不像上述提到的能在dealloc统一移除。
咱们知道,在Objective-C中,对nil发送消息是没有问题的,例如
[thing doStuff];
这种写法没有问题,可是若是参数是nil,则取决于具体的方法是如何实现的,例如:
[self doStuff:thing];
这种状况就要看thing是拿来作什么,若是方法实现里有以下代码
menuItem.title = thing;
menuItem是NSMenuItem,那么当thing为空时就会致使crash。
一种推荐的作法是使用断言对参数作空的判断,具体以下:
- (void)someMethod:(id)someParameter {
NSParameterAssert(someParameter);
…do whatever…
}复制代码
常见的越界crash就是数组越界,固然还有其余的越界,好比NSrange,对于这些的使用,推荐的作法是在使用前都作一下范围校验,这也是须要注意的点。
在非主线程处理UI事件会致使不可预知的事情发生,有可能crash,有多是UI显示异常。好比咱们在子线程执行了一段耗时的计算任务,而后将计算结果传递给UI去更新显示,这时候咱们须要
dispatch_async(dispatch_get_main_queue(), ^{
});复制代码
另外,原文做者还提出了一些他的编程实践经验,例如:
参考资料:inessential.com/hownottocra…
最后作个推广,欢迎关注公众号 MrPeakTech,我从这里学到不少,推荐给你们,共同进步~