-[NSNull objectForKeyedSubscript:]: unrecognized selector sent to instance 0xcf3238
-[NSNull length]: unrecognized selector sent to instance 0x388a4a70
什么場景下會遇到null呢锹引?
nil/Nil/null/NSNull全解
1、解析 JSON 數(shù)據(jù)時助隧。因為在后端數(shù)據(jù)庫里面筑凫,默認數(shù)據(jù)為 null 滑沧,如果修過數(shù)據(jù)后,又刪除巍实,那么數(shù)據(jù)庫會自動補充上 null 滓技。在接口返回數(shù)據(jù)時,就會把 NSNull 傳給我們棚潦,解析出來就是 null 空對象令漂。后端也可以做些調(diào)整,修改默認數(shù)據(jù)的方式:在創(chuàng)建表的時候丸边,添加上 'not null default' 叠必。
2、項目中為了向字典妹窖、數(shù)組纬朝、集合等存入空值,會使用到 NSNull 骄呼,在讀取時共苛,會讀取到 null 。
當我們給一個 null( NSNull 對象)發(fā)送消息的話蜓萄,很大可能會直接Crash( null 是有內(nèi)存的)隅茎,而發(fā)送給nil的話,是不會崩潰的绕德。
解決方案
1患膛、對可能出現(xiàn)空的字段進行非空判斷
會造成 Crash 的字段解析成的對象是 NSNull 類型的,所以可以直接判斷是不是此類型耻蛇,這樣的字段會比較多所以也會比較繁瑣踪蹬,也容易遺漏,但是也比較保險臣咖。
if (![object isKindOfClass:[NSNull class]]){
.....
}
2跃捣、字符串匹配, 替換 null 為 為空字符 ""
在獲取到服務器返回的 JSON 時,返回結(jié)果時 string 對象夺蛇,于是就先替換 null 為 為空字符""疚漆,然后再解析可避免 Crash 問題,這種方法比較巧妙刁赦,但若服務器的數(shù)據(jù)不太整潔也會有一定的問題娶聘。
json = [jsonStr stringByReplacingOccurrencesOfString:@":null" withString:@":\"\""];
3、解析數(shù)據(jù)時把 NSNull 類型的值替換成 nil
使用宏定義甚脉。
#define VerifyValue(value)\
({id tmp;\
if ([value isKindOfClass:[NSNull class]])\
tmp = nil;\
else\
tmp = value;\
tmp;\
})\
在解析數(shù)據(jù)時丸升,把接收到的 NSNull 類型的值替換成 nil 。
contact.contactPhone = VerifyValue(contactDic[@"send_ContactPhone"]);
4牺氨、使用 AFNetworking 提供的方法
如果是使用 AFNetworking 這個庫做網(wǎng)絡請求的話狡耻,可以用以下代碼墩剖,自動幫你去掉空值
self.removesKeysWithNullValues = YES;
5、第三方庫 NullSafe
NullSafe 是一個 Category 夷狰,在運行時操作岭皂,把這個討厭的空值置為 nil ,而 nil 是安全的沼头,可以向 nil 對象發(fā)送任何 message 而不會 Crash 爷绘,其原理便是使用消息轉(zhuǎn)發(fā)機制實現(xiàn)。只需要將文件 nullSafe.m 導入工程便可以瘫证,無需引用頭文件等揉阎。 消息轉(zhuǎn)發(fā)原理淺析
附: NullSafe 的工作原理
當我們給一個 NSNull 對象發(fā)送消息的話庄撮,可能會崩潰( null 是有內(nèi)存的)背捌,而發(fā)送給 nil 的話,是不會崩潰的洞斯。發(fā)送給 NSNull 而 NSNull 又無法處理的消息要經(jīng)過如下幾步做轉(zhuǎn)發(fā)處理:
- 創(chuàng)建一個方法緩存毡庆,這個緩存會緩存項目中類的所有類名。
- 遍歷緩存烙如,尋找是否已經(jīng)有可以執(zhí)行此方法的類么抗。
- 如果有的話,返回這個 NSMethodSignature 亚铁。
- 如果沒有的話蝇刀,返回 nil , 接下來會走 forwardInvocation:方法。
- [invocation invokeWithTarget:nil]; 將消息轉(zhuǎn)發(fā)給 nil徘溢。
那么吞琐,如何判斷NSNull無法處理這個消息呢,在OC中然爆,系統(tǒng)如果對某個實例發(fā)送消息之后站粟,它(及其父類)無法處理(比如,沒有這個方法等)曾雕,系統(tǒng)就會發(fā)送 methodSignatureForSelector 消息奴烙,如果這個方法返回非空,那么就去執(zhí)行返回的方法剖张,如果為 nil, 則發(fā)送 forwardInvocation 消息切诀。這樣就完成整個轉(zhuǎn)發(fā)鏈了。