這篇文章列出了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,我從這裏學到不少,推薦給你們,共同進步~