在OC中存在__nullable
與之對應的__nonnull
修飾符,蘋果為了避免與第三方庫潛在的沖突把 __nonnull/__nullable
改成 _Nonnull/_Nullable
。同時蘋果同樣還支持沒有下劃線的寫法 nonnull/nullable
,這三種寫法本質上都是互通的,只是放的位置不同,意義用途如下:
屬性 | 表示意義 | 用途 | 區別 |
---|---|---|---|
__nullable 、_Nullable、nullable | 表示對象可以是 NULL 或 nil | 方法返回值修飾、參數修飾、聲明屬性修飾 | 放置位置不同 |
__nonnull、_Nonnull、nonnull | 表示對象不應該為空 | 同上 | 同上 |
使用示例:
- 方法返回值修飾:
- (nullable NSString *)function; - (NSString * __nullable) function; - (NSString * _Nullable) function;
- 聲明屬性的修飾:
@property (nonatomic, copy, nullable) NSString * param; @property (nonatomic, copy) NSString * __nullable param; @property (nonatomic, copy) NSString * _Nullable param;
- 方法參數修飾:
- (void)functionWithParam:(nullable NSString *) param; - (void)functionWithParam:(NSString * _Nullable) param; - (void)functionWithParam:(NSString * __nullable) param;
而對于 雙指針類型對象
、Block 的返回值
、Block 的參數
等,這時候就不能用 nonnull/nullable
修飾,只能用帶下劃線的 __nonnull/__nullable
或者 _Nonnull/_Nullable
:
- (void)functionWithError:(NSError * _Nullable * _Nullable)error
- (void)functionWithError:(NSError * __nullable * __null_unspecified)error;
- (void)functionWithBlock:(nullable(整體修飾符位置-外界傳入block是否可以為空) id __nonnull(返回修飾符位置-block返回值是否可以為空) (^)(id __nullable(參數修飾符位置) params))block;
// 上面的 nullable 用于修飾方法傳入的參數 Block 可以為空,__nonnull 用于修飾 Block 返回值 id 不能為空;
根據原生 iOS SDK 里 Foundation 和 UIKit 的頭文件以及蘋果的博文《Nullability and Objective-C》,總結如下使用規范:
-
對于屬性、方法返回值、方法參數的修飾,使用:
nonnull/nullable
; -
對于 C 函數的參數、Block 的參數、Block 返回值的修飾,使用:
_Nonnull/_Nullable
,建議棄用。__nonnull/__nullable
Nonnull Audited Regions
如果每個屬性或每個方法都去指定 nonnull
和 nullable
,將是一件非常繁瑣的事。蘋果為了減輕我們的工作量,專門提供了兩個宏:NS_ASSUME_NONNULL_BEGIN
和 NS_ASSUME_NONNULL_END
。在這兩個宏之間的代碼,所有簡單指針對象都被假定為 nonnull
,因此我們只需要去指定那些 nullable
指針對象即可。如下代碼所示:
NS_ASSUME_NONNULL_BEGIN
@interface myClass ()
@property (nonatomic, copy) NSString * param;
- (id)functionWithParam:(nullable NSString *)param;
@end
NS_ASSUME_NONNULL_END
在上面的代碼中,param
屬性默認是 nonnull
的,functionWithParam:
方法的返回值也是 nonnull
,而方法的參數 str
被顯式指定為 nullable
。
為了安全起見,蘋果還制定了以下幾條規則:
- 通過
typedef
定義的類型的nullability
特性通常依賴于上下文,即使是在 Audited Regions 中,也不能假定它為nonnull
; - 對于復雜的指針類型(如
id *
)必須顯式去指定是nonnull
還是nullable
。例如,指定一個指向nullable
對象的nonnull
指針,可以使用__nullable id * __nonnull
; - 我們經常使用的
NSError **
通常是被假定為一個指向nullable
NSError 對象的nullable
指針。