Crash in Cocoa
Cocoa中會導(dǎo)致Crash的地方:
Exceptions類型
1. 集合類越界或插入Nil:
-
數(shù)組類型
越界訪問會crash
-
字典類型
查詢時:
- (nullable ObjectType)objectForKey:(KeyType)aKey;
當(dāng)key為nil。能夠正常運行废亭。
插入時:
- (void)setObject:(ObjectType)anObject forKey:(KeyType <NSCopying>)aKey;
插入時object和key任一為nil,都會crash
-
字符串類型
獲取substring時,越界訪問會crash.
包括:
- (NSString *)substringFromIndex:(NSUInteger)from; - (NSString *)substringToIndex:(NSUInteger)to; - (NSString *)substringWithRange:(NSRange)range;
2. 訪問懸掛指針(Dangling pointer)
懸掛指針,通常也被稱為野指針粹湃。野指針,指的是指向不正確內(nèi)存的指針。如果我們訪問了不正確的內(nèi)存,則會導(dǎo)致Crash配猫。
那么什么情況下會導(dǎo)致野指針呢?
-
訪問了已被release,但尚未置空的指針
在MRC時代,如果我們提前釋放了對象,并且沒有把對象置空,再訪問這個對象,則會Crash。
Person *mango = [[Person alloc]init]; [mango release]; //object此時為野指針 [mango setName:@"mango"]; // Crash:訪問了野指針 //正確做法 Person *mango = [[Person alloc]init]; [mango release] //object此時為野指針 mango = nil; [mango setName:@"mango"]; //向nil發(fā)送消息,沒有問題
在ARC時代,編譯器會我們進(jìn)行引用計數(shù)的管理盔几。聲明為strong,weak的屬性,在對象引用計數(shù)為零后,自動釋放內(nèi)存,同時將指針置為nil墅冷。
那是否就萬事大吉了呢纯路。
事實上由于歷史原因,UIKit等官方框架里,許多delegate還是聲明為assgin而不是weak。(Apple:其實是我懶得改了 :)
聲明為assgin和unsafe_unretained的對象,內(nèi)存被釋放后,編譯器不會自動將指針置為nil寞忿。
像
NSNetServices
的delegate,官方的頭文件是這樣:@property (assign) id <NSNetServiceBrowserDelegate> delegate;
類似這樣聲明的delegate,如果delegate提前被釋放,但是我們沒有幫助官方的類將delegate置空,如果此時官方的類需要調(diào)用到delegate,則同樣會造成Crash驰唬。
解決方案:
遇到這種舊時代的官方delegate,為了安全起見,我們在dealloc將delegate置為nil
- (void)setService:(NSNetService *)service { _service = service; self.service.delegate = self; [self.service resolveWithTimeout:5]; } - (void)dealloc { // 避免懸掛指針 self.service.delegate = nil; }
3. 為不可為空的函數(shù)參數(shù)賦值nil
例如:
- (NSString *)stringByAppendingString:(NSString *)aString
//aString:
//The string to append to the receiver. This value must not be nil
//NSNotificationCenter
- (void)postNotification:(NSNotification * _Nonnull)notification
//notification
//The notification to post. This value must not be nil.
等等......
這種情況在Swift中已經(jīng)不會再發(fā)生。Swift中Optional機(jī)制的引入腔彰。讓我們不會因為忘記判空而導(dǎo)致Crash叫编。
let cool = testStr.stringByAppendingString(nil)
//error: nil is not compatible with expected argument type 'String'
參考: