iOS 全面理解 Nullability 即 nil, Nil, NULL, NSNull, kCFNulL 及空值修飾符

探究系列已發(fā)布文章列表津畸,有興趣的同學(xué)可以翻閱一下:

第一篇 | iOS 屬性 @property 詳細(xì)探究

第二篇 | iOS 深入理解 Block 使用及原理

第三篇 | iOS 類別 Category 和擴(kuò)展 Extension 及關(guān)聯(lián)對象詳解

第四篇 | iOS 常用鎖 NSLock 逞敷,@synchronized 等的底層實(shí)現(xiàn)詳解

第五篇 | iOS 全面理解 Nullability

------- 正文開始 -------

引言

日常開發(fā)過程中绎狭,我們經(jīng)常會碰到空值、空指針侥涵、空對象沼撕、空的占位對象等。在一些情況下芜飘,如果判斷不好或者處理方式不對务豺,可能會引起程序運(yùn)行異常,有些特殊情況甚至?xí)?dǎo)致 Crash 嗦明,因此冲呢,熟練了解掌握它們之間的區(qū)別,將有助于我們寫出更高質(zhì)量的代碼,本文就詳細(xì)介紹一下它們之間的差別與注意事項(xiàng)敬拓。


Nullability 的由來

Swift 中,我們會使用 ?! 去顯式聲明一個對象或函數(shù)的的參數(shù)及函數(shù)的返回值是 optional 還是 non-optional 裙戏,而在 Objective-C 中則沒有這一區(qū)分乘凸。當(dāng)我們在 Swift 與 Objective-C 混編開發(fā)時(shí),由于 Swift 編譯器并不知道這個 Objective-C 對象或函數(shù)的參數(shù)或者函數(shù)的返回值是 optional 還是 non-optional 累榜,這種情況下編譯器會隱式地都當(dāng)成是 non-optional 來處理营勤,所以經(jīng)常性的會因?yàn)榘岩粋€空值當(dāng)做 non-optional 來處理而導(dǎo)致程序 Crash。

因此為了解決這個問題壹罚,蘋果在 Xcode 6.3CObjective-C 引入了一個新特性: Nullability Annotations 葛作。

Nullability annotations for C and Objective-C are available starting in Xcode 6.3

空值相關(guān)

  • nil
#ifndef nil
# if __has_feature(cxx_nullptr)
#   define nil nullptr
# else
#   define nil __DARWIN_NULL
# endif
#endif

宏: Objective-C 對象使用,表示對象為空 (id)0猖凛。常作為對象的空指針和數(shù)組的結(jié)束標(biāo)志赂蠢。

  • Nil
#ifndef Nil
# if __has_feature(cxx_nullptr)
#   define Nil nullptr
# else
#   define Nil __DARWIN_NULL
# endif
#endif

宏: Objective-C 類使用,表示類指向空 (Class)0辨泳,即類指針為空虱岂。

  • NULL
#if !defined(NULL)
#if defined(__GNUG__)
    #define NULL __null
#elif defined(__cplusplus)
    #define NULL 0
#else
    #define NULL ((void *)0)
#endif
#endif

宏: C 語言指針使用,表示空指針 (void *)0菠红。常在用基本數(shù)據(jù)類型為空情況第岖。

  • NSNull
@interface NSNull : NSObject <NSCopying, NSSecureCoding>
// 返回單例對象
+ (NSNull *)null;

@end

Objective-C 類: 是一個空值的單例對象 [NSNull null],繼承自 NSObject 试溯,可用于表示不允許空值的集合對象中蔑滓。

  • kCFNull
const CFNullRef kCFNull;    // the singleton null instance

單例 CFNull 對象,等于 NSNull 單例


空值修飾符相關(guān):

有些同學(xué)可能奇怪為什么會有這么多寫法遇绞,其實(shí)他們都是兩兩成對出現(xiàn)的:

  • nullable & nonnull

  • _Nullable & _Nonnull

  • __nullable & __nonnull

其中 nonnull 键袱, _Nonnull__nonnull 三個修飾的參數(shù)表示對象不可以是 NULL 或 nil 试读,如果向它們修飾的對象傳 NULL 或 nil 的話杠纵,編譯器會產(chǎn)生警告。

其中 nullable 钩骇, _Nullable 比藻, __nullable 三個修飾的參數(shù)表示對象可以是 NULL 或 nil 。

功能上來說倘屹,它們沒有什么區(qū)別银亲,只是使用上位置有所區(qū)別:

// 不帶下劃線關(guān)鍵詞 nonnull , nullable 不同應(yīng)用場景下的擺放位置
@property(nonnull, nonatomic, copy) NSString *name;
@property(nullable, nonatomic, copy) NSString *company;
- (nonnull NSString *)firstString:(nullable NSString *)str;

// 帶下劃線關(guān)鍵詞 _Nonnull 纽匙,_Nullable 不同應(yīng)用場景下的擺放位置
@property(nonatomic, copy) NSString * _Nonnull name;
@property(nonatomic, copy) NSString * _Nullable company;
- (NSString * _Nonnull)secondString:(NSString * _Nullable)str;

// 帶下劃線關(guān)鍵詞 __nonnull 务蝠,__nullable 不同應(yīng)用場景下的擺放位置
@property(nonatomic, copy) NSString * __nonnull name;
@property(nonatomic, copy) NSString * __nullable company;
- (NSString * __nonnull)thirdString:(NSString * __nullable)str;

關(guān)于 Nullability Annotations 官方介紹:



This feature was first released in Xcode 6.3 with the keywords __nullable and __nonnull. Due to potential conflicts with third-party libraries, we’ve changed them in Xcode 7 to the _Nullable and _Nonnull you see here. However, for compatibility with Xcode 6.3 we’ve predefined macros __nullable and __nonnull to expand to the new names.

此功能首次在 Xcode 6.3 中使用關(guān)鍵字 __nullable 和 __nonnull 。由于與第三方庫的潛在沖突烛缔,我們在 Xcode 7 中將它們更改為您在此處看到的 _Nullable 和 _Nonnull馏段。但是轩拨,為了與 Xcode 6.3 兼容,我們預(yù)定義了宏 __nullable 和 __nonnull 以擴(kuò)展新名稱

蘋果也支持使用沒有下劃線的寫法 nonnull 院喜,nullable 亡蓉,于是就有了三種寫法。

另外我們還會經(jīng)撑缫ǎ看到下面兩個關(guān)鍵詞:

  • null_resettable

  • null_unspecified & __null_unspecified & _Null_unspecified

其中 null_resettable 砍濒,getter 不能返回空,setter 可以為空(注意: 使用 null_resettable 必須重寫 getter 方法和 setter 方法硫麻,處理值為空的情況)爸邢。

其中 null_unspecified__null_unspecified 拿愧, _Null_unspecified 杠河, 表示不確定是否為空。

二者的具體使用:

// null_resettable 具體應(yīng)用
@property (nonatomic, strong, null_resettable) NSString *name;

// 不帶下劃線關(guān)鍵詞 null_unspecified 不同應(yīng)用場景下的擺放位置
@property(null_unspecified, nonatomic, copy) NSString *name;
- (null_unspecified NSString *)firstString:(null_unspecified NSString *)str;

// 帶下劃線關(guān)鍵詞 __null_unspecified 不同應(yīng)用場景下的擺放位置
@property(nonatomic, copy) NSString * __null_unspecified name;
- (NSString * __null_unspecified)secondString:(NSString * __null_unspecified)str;

// 帶下劃線關(guān)鍵詞 _Null_unspecified 不同應(yīng)用場景下的擺放位置
@property(nonatomic, copy) NSString * _Null_unspecified name;
- (NSString * _Null_unspecified)thirdString:(NSString * _Null_unspecified)str;

注意:

  1. 可空性關(guān)鍵詞 nonnull 赶掖,nullable 等只能修飾對象感猛,不能修飾基本數(shù)據(jù)類型。
  2. NS_ASSUME_NONNULL_BEGINNS_ASSUME_NONNULL_END 之間奢赂,定義的所有對象屬性和方法默認(rèn)都是 nonnull 陪白。

拓展知識

  • Sending Messages to nil

用過 Objective-C 開發(fā)的同學(xué)應(yīng)該都非常熟悉這句話,正如官方描述的:

In Objective-C, it is valid to send a message to nil—it simply has no effect at runtime.

這說明 nil 本身可以足夠安全地調(diào)用方法而不會崩潰膳灶。

Swift 中咱士,我們有更多的安全性。我們可以給 nil 發(fā)“消息”(其實(shí)并沒有)轧钓,但前提是它們是鏈?zhǔn)娇蛇x項(xiàng)(chained optional)序厉。只有當(dāng)我們使用可選時(shí),nil 才能成為一個有存在感的“something”毕箍。

  • JSON 數(shù)據(jù)中帶有 null 值情況

解析 JSON 數(shù)據(jù)時(shí)弛房,如果接口返回?cái)?shù)據(jù)中把 NSNull 傳給我們,解析出來就是 null 空對象而柑,如下:

{
  "title": "iOS Engineer",
  "age": 18,
  "name": null
}

這時(shí)當(dāng)我們給 nullNSNull 對象)發(fā)送消息的話文捶,很大可能會直接Crash( null 是有內(nèi)存的)。

如何解決這個問題呢媒咳,通常有一下幾種方式:

  1. 接口端調(diào)整粹排,修改默認(rèn)數(shù)據(jù)的方式,在創(chuàng)建表的時(shí)候涩澡,添加上 'not null default' 顽耳;
  2. 對可能出現(xiàn)空的字段進(jìn)行非空判斷;
if (![object isKindOfClass:[NSNull class]]){
    // ...
}
  1. JSON 進(jìn)行字符串匹配, 替換 null 為空字符 ""(注意: 這個方法可能會有問題);
  2. 解析數(shù)據(jù)時(shí)對類型進(jìn)行檢查射富,并把 NSNull 類型的值替換成 nil膝迎;
  3. 如果使用的是 AFNetworking 請求數(shù)據(jù),可以使用其提供的 ((AFJSONResponseSerializer *)manager.responseSerializer).removesKeysWithNullValues = YES; 去掉空值胰耗;
  4. 使用第三方庫 NullSafe弄抬,它是 NSNull 上的一個簡單的 category ,為無法識別的消息返回 nil 宪郊,其代碼實(shí)現(xiàn)非常簡單,具體可見源碼: NullSafe ( https://github.com/nicklockwood/NullSafe )

總結(jié)

隨著越來越多的項(xiàng)目使用 Objective-CSwift 混編開發(fā)拖陆,Nullability 越來越需要大家引起重視弛槐,一旦使用不當(dāng),可能在代碼的某個角落就會出現(xiàn)一個 bug 甚至導(dǎo)致 App Crash依啰。以上就是本文對 iOS Nullability 相關(guān)知識點(diǎn)的介紹乎串,希望這篇文章對你有所幫助,感謝閱讀速警。


參考資料:


關(guān)于技術(shù)組

iOS 技術(shù)組主要用來學(xué)習(xí)叹誉、分享日常開發(fā)中使用到的技術(shù),一起保持學(xué)習(xí)闷旧,保持進(jìn)步长豁。文章倉庫在這里:https://github.com/minhechen/iOSTechTeam
微信公眾號:iOS技術(shù)組,歡迎聯(lián)系進(jìn)群學(xué)習(xí)交流忙灼,感謝閱讀匠襟。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市该园,隨后出現(xiàn)的幾起案子酸舍,更是在濱河造成了極大的恐慌,老刑警劉巖里初,帶你破解...
    沈念sama閱讀 218,122評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件啃勉,死亡現(xiàn)場離奇詭異,居然都是意外死亡双妨,警方通過查閱死者的電腦和手機(jī)淮阐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來斥难,“玉大人枝嘶,你說我怎么就攤上這事⊙普铮” “怎么了群扶?”我有些...
    開封第一講書人閱讀 164,491評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么娩梨? 我笑而不...
    開封第一講書人閱讀 58,636評論 1 293
  • 正文 為了忘掉前任祖驱,我火速辦了婚禮,結(jié)果婚禮上醇份,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好丢氢,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著先改,像睡著了一般疚察。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上仇奶,一...
    開封第一講書人閱讀 51,541評論 1 305
  • 那天貌嫡,我揣著相機(jī)與錄音,去河邊找鬼该溯。 笑死岛抄,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的狈茉。 我是一名探鬼主播夫椭,決...
    沈念sama閱讀 40,292評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼论皆!你這毒婦竟也來了益楼?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,211評論 0 276
  • 序言:老撾萬榮一對情侶失蹤点晴,失蹤者是張志新(化名)和其女友劉穎感凤,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體粒督,經(jīng)...
    沈念sama閱讀 45,655評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡陪竿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了屠橄。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片族跛。...
    茶點(diǎn)故事閱讀 39,965評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖锐墙,靈堂內(nèi)的尸體忽然破棺而出礁哄,到底是詐尸還是另有隱情,我是刑警寧澤溪北,帶...
    沈念sama閱讀 35,684評論 5 347
  • 正文 年R本政府宣布桐绒,位于F島的核電站夺脾,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏茉继。R本人自食惡果不足惜咧叭,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望烁竭。 院中可真熱鬧菲茬,春花似錦、人聲如沸派撕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽终吼。三九已至马胧,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間衔峰,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評論 1 269
  • 我被黑心中介騙來泰國打工蛙粘, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留垫卤,地道東北人。 一個月前我還...
    沈念sama閱讀 48,126評論 3 370
  • 正文 我出身青樓出牧,卻偏偏與公主長得像穴肘,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子舔痕,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評論 2 355

推薦閱讀更多精彩內(nèi)容