
本文由CocoaChina譯者星夜暮晨翻譯
原文:CLANG WARN NULLABLE TO NONNULL CONVERSION
Xcode 7 包含了一個名為 “Apple LLVM 7.0 - Warnings - All languages > Incorrect Uses of Nullable Values” 的項目設置選項,其鍵為 CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION。

您可以使用 -Wnullable-to-nonnull-conversion 將其作為編譯器標志(compiler flag)進行傳遞。
為了向大家更好地說明,試想有這麼一個命令行應用:
#import void welcomeToClowntown(NSString *_Nonnull greeting) {
NSLog(@"%@, welcome to clowntown!", greeting);
}
int main(int argc, const char * argv[]) {
// Warning: Null passed to a callee that requires
// a non-null argument
welcomeToClowntown(nil);
return 0;
}注意到我們向函數 welcomeToClowntown() 中傳遞了一個 nil 字面量,即使這個函數接收的參數類型是 NSString *_Nonnull。不管 CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION 有無開啟,這都會引發一個警告。
但是下面這個例子又會如何呢?
#import void welcomeToClowntown(NSString *_Nonnull greeting) {
NSLog(@"%@, welcome to clowntown!", greeting);
}
NSString *_Nullable clownyGreeting() {
if (arc4random() % 2 == 0) {
return @"Hola";
} else {
return nil;
}
}
int main(int argc, const char * argv[]) {
// *只有*在
// CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION
// 開啟的時候才會引發警告
//
// Warning: Implicit conversion from nullable pointer
// 'NSString * _Nullable' to non-nullable pointer
// type 'NSString * _Nonnull'
welcomeToClowntown(clownyGreeting());
return 0;
}welcomeToClowntown() 需要傳遞一個 NSString *_Nonnull 類型的參數,然而我們為其傳遞的是 clownyGreeting() 函數的返回值,其類型是 NSString *_Nullable。換句話說,我們給一個需要非 nil 值的函數傳遞了一個可能為 nil 的值。CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION 針對這種情況會引發一個警告。
我建議在向 Objective-C 頭文件中添加可空標識符之前啟用這個功能。這樣,只要你給你的接口添加了不可空值的限制,那麼編譯器就會在違反這些限制的時候彈出警告。
並不是。和大多數編譯器警告相仿,它可以被規避掉:
#import void welcomeToClowntown(NSString *_Nonnull greeting) {
NSLog(@"%@, welcome to clowntown!", greeting);
}
NSString *_Nullable clownyGreeting() {
if (arc4random() % 2 == 0) {
return @"Hola";
} else {
return nil;
}
}
int main(int argc, const char * argv[]) {
// 不再引發警告,因為返回類型
// 被“強轉”為了一個(未明確聲明為不可空的) `NSString *`類型。
NSString *greeting = clownyGreeting();
welcomeToClowntown(greeting);
return 0;
}當我們獲取 clownyGreeting() 的值時,我們得到的是一個 NSString *_Nonnull,然後我們將其賦給了 NSString * 類型的變量,這導致編譯器不再提示警告。我想這是由於返回的 NSString *_Nonnull 對象被“強轉”為了 NSString * 類型,而這個類型沒有明確聲明是否可為空。
可空標識符被添加到了諸如 NSString 之類的 Foundation 類當中,它們當中的某些判斷非常嚴格。比如說,-[NSString isEqualToString:] 接收的參數是 NSString *_Nonnull 類型。如果你有許多該方法的調用點,並且期望當傳入的參數為 nil 的時候,-isEqualToString: 將返回 NO 值(或許 JSON 解析之類的?),你就不得不修改這些調用點來消滅惱人的警告。
#import NSString *_Nullable clownyGreeting() {
if (arc4random() % 2 == 0) {
return @"Hola";
} else {
return nil;
}
}
int main(int argc, const char * argv[]) {
// *只有*在
// CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION
// 開啟的時候才會引發警告
//
// Warning: Implicit conversion from nullable pointer
// 'NSString * _Nullable' to non-nullable pointer
// type 'NSString * _Nonnull'
[@"Hola" isEqualToString:clownyGreeting()];
return 0;
}這會讓編碼工作變得更為麻煩,因此注意:在項目中開啟 CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION 功能將會導致大量的警告出現!
不過,這些為 nil 的錯誤提示點或許會遍布於你的整個系統。你可以試著將這個功能打開,然後看看令人吃驚的結果吧!
感謝 @nlutsenko(https://twitter.com/nlutsenko) 為我介紹了這個設置項!
最新版本的 Clang 靜態分析器在2015年10月28號的時候發布。它能夠捕獲到我上面指出的“可規避點”的問題:
#import void welcomeToClowntown(NSString *_Nonnull greeting) {
NSLog(@"%@, welcome to clowntown!", greeting);
}
NSString *_Nullable clownyGreeting() {
if (arc4random() % 2 == 0) {
return @"Hola";
} else {
return nil;
}
}
int main(int argc, const char * argv[]) {
// 此前不會引發警告,因為返回類型
// 被“強轉”為了一個(未明確聲明為不可空的) `NSString *`類型。
//
// 最新的 Clang 靜態分析器則會引發警告:
// Warning: Nullable pointer is passed to a callee
// that requires a non-null argument
NSString *greeting = clownyGreeting();
welcomeToClowntown(greeting);
return 0;
}這是一項極大的改進,我已等不及使用和 Xcode 共存的這個新分析器了。當然,你也可以下載最新的版本並運行以下代碼,就可以自行使用了。
$ checker-277/scan-build \ -enable-checker nullability.NullPassedToNonnull \ -enable-checker nullability.NullReturnedFromNonnull \ -enable-checker nullability.NullableDereferenced \ -enable-checker nullability.NullablePassedToNonnull \ xcodebuild
本文中的所有譯文僅用於學習和交流目的,轉載請注明文章譯者、出處、和本文鏈接。
感謝博文視點為本期翻譯活動提供贊助
