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