本文由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
本文中的所有译文仅用于学习和交流目的,转载请注明文章译者、出处、和本文链接。
感谢 博文视点 为本期翻译活动提供赞助