探究系列已發(fā)布文章列表津畸,有興趣的同學(xué)可以翻閱一下:
第一篇 | iOS 屬性 @property 詳細(xì)探究
第三篇 | iOS 類別 Category 和擴(kuò)展 Extension 及關(guān)聯(lián)對象詳解
第四篇 | iOS 常用鎖 NSLock 逞敷,@synchronized 等的底層實(shí)現(xiàn)詳解
------- 正文開始 -------
引言
日常開發(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.3
為 C
及 Objective-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;
注意:
- 可空性關(guān)鍵詞
nonnull
赶掖,nullable
等只能修飾對象感猛,不能修飾基本數(shù)據(jù)類型。 - 在
NS_ASSUME_NONNULL_BEGIN
和NS_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)我們給 null
( NSNull
對象)發(fā)送消息的話文捶,很大可能會直接Crash( null
是有內(nèi)存的)。
如何解決這個問題呢媒咳,通常有一下幾種方式:
- 接口端調(diào)整粹排,修改默認(rèn)數(shù)據(jù)的方式,在創(chuàng)建表的時(shí)候涩澡,添加上 'not null default' 顽耳;
- 對可能出現(xiàn)空的字段進(jìn)行非空判斷;
if (![object isKindOfClass:[NSNull class]]){
// ...
}
- 對
JSON
進(jìn)行字符串匹配, 替換null
為空字符 ""(注意: 這個方法可能會有問題); - 解析數(shù)據(jù)時(shí)對類型進(jìn)行檢查射富,并把
NSNull
類型的值替換成nil
膝迎; - 如果使用的是
AFNetworking
請求數(shù)據(jù),可以使用其提供的((AFJSONResponseSerializer *)manager.responseSerializer).removesKeysWithNullValues = YES;
去掉空值胰耗; - 使用第三方庫
NullSafe
弄抬,它是NSNull
上的一個簡單的category
,為無法識別的消息返回nil
宪郊,其代碼實(shí)現(xiàn)非常簡單,具體可見源碼: NullSafe ( https://github.com/nicklockwood/NullSafe )
總結(jié)
隨著越來越多的項(xiàng)目使用 Objective-C
與 Swift
混編開發(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í)交流忙灼,感謝閱讀匠襟。