蘋果在 Xcode 6.3 引入了一個(gè) Objective-C 的新特性:Nullability Annotations柑蛇,這一新特性的核心是兩個(gè)新的類型修飾: __nullable 和 __nonnull 。從字面上我們可知, __nullable 表示對(duì)象可以是 NULL 或 nil,而__nonnull 表示對(duì)象不應(yīng)該為空。當(dāng)我們不遵循這一規(guī)則時(shí)窃诉,編譯器就會(huì)給出警告。
在 Xcode 7 中赤套,為了避免與第三方庫(kù)潛在的沖突飘痛,蘋果把 __nonnull/__nullable改成 _Nonnull/_Nullable 。再加上蘋果同樣支持了沒(méi)有下劃線的寫法 nonnull/nullable 容握,于是就造成現(xiàn)在有三種寫法這樣混亂的局面宣脉。但是這三種寫法本質(zhì)上都是互通的,只是放的位置不同剔氏,舉例如下:
- 方法返回值修飾:
- (nullable NSString *)method;
- (NSString * __nullable)method;
- (NSString * _Nullable)method;
- 聲明屬性的修飾:
@property (nonatomic, copy, nullable) NSString *aString;
@property (nonatomic, copy) NSString * __nullablea String;
@property (nonatomic, copy) NSString * _Nullable aString;
- 方法參數(shù)修飾:
- (void)methodWithString:(nullable NSString*)aString;
- (void)methodWithString:(NSString *_Nullable)aString;
- (void)methodWithString:(NSString *__nullable)aString;
- 而對(duì)于雙指針類型對(duì)象 塑猖、 Block 的返回值 、 Block 的參數(shù)等谈跛,這時(shí)候就不能用 nonnull/nullable 修飾羊苟,只能用帶下劃線的 __nonnull/__nullable 或者 _Nonnull/_Nullable :
- (void)methodWithError:(NSError* _Nullable * _Nullable)error
- (void)methodWithError:(NSError* __nullable* __null_unspecified)error;
- 以及其他的組合方式
- (void)methodWithBlock:(nullable void(^)())block;
注意上面的 nullable 用于修飾方法傳入的參數(shù) Block 可以為空,而不是修飾 Block 返回值感憾;
- (void)methodWithBlock:(void(^ _Nullable)())block;
- (void)methodWithBlock:(void(^ __nullable)())block;
- (void)methodWithBlock:(nullable id __nonnull(^)(id __nullable params))block;
注意上面的 nullable 用于修飾方法傳入的參數(shù) Block 可以為空蜡励,而 __nonnull 用于修飾 Block 返回值 id 不能為空
- (void)methodWithBlock:(id __nonnull(^ __nullable)(id __nullable params))block;
- (void)methodWithBlock:(id _Nonnull (^ _Nullable)(id _Nullable params))block;
以上基本上羅列了絕大部分的使用場(chǎng)景,但看完我們還是一臉懵逼啊阻桅,仍然不清楚什么時(shí)候應(yīng)該用哪個(gè)修飾符凉倚!
在看了原生 iOS SDK 里 Foundation 和 UIKit 的頭文件以及蘋果的博文 《Nullability and Objective-C》 ,我們總結(jié)如下使用規(guī)范:
對(duì)于屬性嫂沉、方法返回值稽寒、方法參數(shù)的修飾,使用:nonnull/nullable输瓜;
對(duì)于 C 函數(shù)的參數(shù)瓦胎、Block 的參數(shù)芬萍、Block 返回值的修飾,使用:_Nonnull/_Nullable搔啊,建議棄用__nonnull/__nullable柬祠。
如果需要每個(gè)屬性或每個(gè)方法都去指定 nonnull 和 nullable ,將是一件非常繁瑣的事负芋。蘋果為了減輕我們的工作量漫蛔,專門提供了兩個(gè)宏: NS_ASSUME_NONNULL_BEGIN和NS_ASSUME_NONNULL_END。在這兩個(gè)宏之間的代碼旧蛾,所有簡(jiǎn)單指針對(duì)象都被假定為nonnull莽龟,因此我們只需要去指定那些nullable指針對(duì)象即可。如下代碼所示:
NS_ASSUME_NONNULL_BEGIN
@interface MyClass()
@property(nonatomic, copy) NSString *aString;
- (id)methodWithString:(nullable NSString*)str;
@end
NS_ASSUME_NONNULL_END
在上面的代碼中锨天,aString屬性默認(rèn)是nonnull的毯盈,methodWithString:方法的返回值也是nonnull,而方法的參數(shù)str被顯式指定為nullable病袄。
不過(guò)搂赋,為了安全起見(jiàn),蘋果還制定了以下幾條規(guī)則:
通過(guò)typedef定義的類型的nullability特性通常依賴于上下文益缠,即使是在 Audited Regions 中脑奠,也不能假定它為nonnull;
對(duì)于復(fù)雜的指針類型(如id *)必須顯式去指定是nonnull還是nullable幅慌。例如宋欺,指定一個(gè)指向nullable對(duì)象的nonnull指針,可以使用__nullable id * __nonnull胰伍;
我們經(jīng)常使用的NSError齿诞,通常是被假定為一個(gè)指向nullableNSError 對(duì)象的nullable指針。