本文由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
本文中的所有譯文僅用於學習和交流目的,轉載請注明文章譯者、出處、和本文鏈接。
感謝博文視點為本期翻譯活動提供贊助