【发布时间】:2017-07-15 19:28:48
【问题描述】:
当使用-Wnullable-to-nonnull-conversion 进行编译时,我们会收到带有以下代码的适当警告:
NSString * _Nullable maybeFoo = @"foo";
^(NSString * _Nonnull bar) {
}(maybeFoo);
Tests.m:32:7: error: implicit conversion from nullable pointer 'NSString * _Nullable' to non-nullable pointer type 'NSString * _Nonnull' [-Werror,-Wnullable-to-nonnull-conversion]
}(maybeFoo);
^
1 error generated.
如何安全地将 foo 从 NSString * _Nullable 转换为 NSString * _Nonnull?
目前为止我最好的解决方案
我想出的最好的就是这个宏:
#define ForceUnwrap(type, nullableExpression) ^type _Nonnull () { \
type _Nullable maybeValue___ = nullableExpression; \
if (maybeValue___) { \
return (type _Nonnull) maybeValue___; \
} else { \
NSLog(@"Attempted to force unwrap a null: " #nullableExpression); \
abort(); \
} \
}()
使用如下:
NSString * _Nullable maybeFoo = @"foo";
if (maybeFoo) {
NSString * _Nonnull foo = ForceUnwrap(NSString *, maybeFoo);
^(NSString * _Nonnull bar) {
}(foo);
}
如果分配给错误类型的变量会产生错误:
NSString * _Nullable maybeFoo = @"foo";
if (maybeFoo) {
NSNumber * _Nonnull foo = ForceUnwrap(NSString *, maybeFoo);
^(NSNumber * _Nonnull bar) {
}(foo);
}
Tests.m:40:29: error: incompatible pointer types initializing 'NSNumber * _Nonnull' with an expression of type 'NSString * _Nonnull' [-Werror,-Wincompatible-pointer-types]
NSNumber * _Nonnull foo = ForceUnwrap(NSString *, maybeFoo);
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 error generated.
如果转换为错误的类型会产生错误:
NSString * _Nullable maybeFoo = @"foo";
if (maybeFoo) {
NSNumber * _Nonnull foo = ForceUnwrap(NSNumber *, maybeFoo);
^(NSNumber * _Nonnull bar) {
}(foo);
}
Tests.m:40:35: error: incompatible pointer types initializing 'NSNumber * _Nullable' with an expression of type 'NSString * _Nullable' [-Werror,-Wincompatible-pointer-types]
NSNumber * _Nonnull foo = ForceUnwrap(NSNumber *, maybeFoo);
^ ~~~~~~~~
Tests.m:27:16: note: expanded from macro 'ForceUnwrap'
type _Nullable maybeValue___ = nullableExpression; \
^ ~~~~~~~~~~~~~~~~~~
1 error generated.
不幸的是,如果您需要转换为具有多个参数的泛型类型,则必须求助于preprocessor hacks:
NSDictionary<NSString *, NSString *> * _Nullable maybeFoo =
[NSDictionary<NSString *, NSString *> new];
if (maybeFoo) {
NSDictionary<NSString *, NSString *> * _Nonnull foo =
#define COMMA ,
ForceUnwrap(NSDictionary<NSString * COMMMA NSString *>, maybeFoo);
#undef COMMA
^(NSDictionary<NSString *, NSString *> * _Nonnull bar) {
}(foo);
}
我尝试过的方法不起作用
将maybeFoo 直接分配给NSString * _Nonnull 不起作用。它产生与以前相同的错误:
NSString * _Nullable maybeFoo = @"foo";
if (maybeFoo) {
NSString * _Nonnull foo = maybeFoo;
^(NSString * _Nonnull bar) {
}(foo);
}
Tests.m:30:35: error: implicit conversion from nullable pointer 'NSString * _Nullable' to non-nullable pointer type 'NSString * _Nonnull' [-Werror,-Wnullable-to-nonnull-conversion]
NSString * _Nonnull foo = maybeFoo;
^
1 error generated.
将maybeFoo 转换为NSString * _Nonnull 是不安全的,因为如果maybeFoo 的类型发生变化,编译器不会中断:
NSNumber * _Nullable maybeFoo = @"foo";
if (maybeFoo) {
NSString * _Nonnull foo = (NSString * _Nonnull) maybeFoo;
^(NSString * _Nonnull bar) {
}(foo);
}
// no errors!
我也尝试在转换时使用__typeof__,但__typeof__ 带有可空性说明符,因此当您尝试转换为__typeof__(maybeFoo) _Nonnull 时会遇到可空性冲突:
NSString * _Nullable maybeFoo = @"foo";
if (maybeFoo) {
NSString * _Nonnull foo = (__typeof__(maybeFoo) _Nonnull) maybeFoo;
^(NSString * _Nonnull bar) {
}(foo);
}
Tests.m:30:57: error: nullability specifier '_Nonnull' conflicts with existing specifier '_Nullable'
NSString * _Nonnull foo = (__typeof__(maybeFoo) _Nonnull) maybeFoo;
^
Tests.m:30:35: error: implicit conversion from nullable pointer 'NSString * _Nullable' to non-nullable pointer type 'NSString * _Nonnull' [-Werror,-Wnullable-to-nonnull-conversion]
NSString * _Nonnull foo = (__typeof__(maybeFoo) _Nonnull) maybeFoo;
^
2 errors generated.
一切都使用深度静态分析器运行,并使用 Xcode 8.2.1 编译,并带有以下标志:
-Wnon-modular-include-in-framework-module
-Werror=non-modular-include-in-framework-module
-Wno-trigraphs
-Werror
-Wno-missing-field-initializers
-Wno-missing-prototypes
-Wunreachable-code
-Wno-implicit-atomic-properties
-Wno-arc-repeated-use-of-weak
-Wduplicate-method-match
-Wno-missing-braces
-Wparentheses
-Wswitch
-Wunused-function
-Wno-unused-label
-Wno-unused-parameter
-Wunused-variable
-Wunused-value
-Wempty-body
-Wuninitialized
-Wno-unknown-pragmas
-Wno-shadow
-Wno-four-char-constants
-Wno-conversion
-Wconstant-conversion
-Wint-conversion
-Wbool-conversion
-Wenum-conversion
-Wshorten-64-to-32
-Wpointer-sign
-Wno-newline-eof
-Wno-selector
-Wno-strict-selector-match
-Wundeclared-selector
-Wno-deprecated-implementations
-Wno-sign-conversion
-Wno-infinite-recursion
-Weverything
-Wno-auto-import
-Wno-objc-missing-property-synthesis
-Wno-cstring-format-directive
-Wno-direct-ivar-access
-Wno-double-promotion
【问题讨论】:
-
你没有。不要为 Objective-C 使用该属性。它们适用于 Swift。 Objective-C 对 nil 行为有明确定义的信息。
-
它们仅适用于 Swift。编译器不会更改任何生成的代码,具体取决于引用的可空性。
-
Again,正如 Amin 所说,“非空”在 ObjC 中不是一个东西。观察;使用尽可能多的标志编译以下内容。 -Wall,-Weverything,-Wpedantic,-Werror。
NSString * __nonnull s = nil;编译良好。 ObjC 永远不会强制执行可空性。nil对象指针的行为是基本且成熟的。 -
@Caswell,你错了。如果您使用
-Wnulllable-to-nonnull-conversion编译,NSString * _Nonnull s = nil;将收到警告。 -
对不起,阿明,但这只是无知。当然编译器输出不会改变。如果这是唯一重要的事情,您可以忽略大多数警告。但是,如果您想编写干净的代码以防止您在实际使用它们之前出现很多问题,那么可空性当然会为您的代码增加很多安全性。不仅在 Swift 中。
标签: ios objective-c xcode nullable objective-c-nullability