【问题标题】:Clang nullability warnings and how to approach themClang 可空性警告以及如何处理它们
【发布时间】:2016-07-17 11:42:40
【问题描述】:

最近我在 Xcode 中打开了CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION,但我的 Objective-C 代码中出现了与可空性相关的警告,这让我不知所措。最流行的一种警告类型是Implicit conversion from nullable pointer 'TypeA * _Nullable' to non-nullable pointer type 'TypeA * _Nonnull'

我开始尝试通过在此处描述的方法中创建相同类型的本地来删除这些警告。 https://www.mail-archive.com/xcode-users%40lists.apple.com/msg02260.html 这篇文章说,首先使用一个本地对象,该对象的属性未指定可以为空,因此它可以用作期望非空的方法的合法参数。

但我觉得这是一种逃避行为,实际上并没有以任何有益的方式解决问题。

有人做过这个练习吗?如果您能分享您采取的策略,我将不胜感激。

【问题讨论】:

  • 您要解决什么问题?当您知道对象不是 nil 时抑制警告?还是确保对象不为 nil 的实际过程?
  • @dan 假设我有如下代码:NSMutableData* someData = [NSMutableData dataWithCapacity:d1000];[someData appendData:[NSString stringWithFormat:@"I like to win $%d", 1000000]];。因为appendData: 期望一个非空值,所以解决方法是在调用之前首先创建 NSString*。理想情况下,我不想这样做,因为它只是添加了试图解决问题的额外代码行,并没有真正给我带来任何好处。
  • 好处是警告不再存在。这是否值得额外的代码行取决于您。我认为它没有任何问题。
  • [NSString stringWithFormat:] 可以返回 nil 或者不能。无论哪种方式,我都不明白为什么分配给中间变量会有所作为。如果编译器假设这样的变量不是nil,则编译器已损坏,我将禁用此警告。

标签: ios objective-c xcode objective-c-nullability


【解决方案1】:

实际上,我已经把这个话题搞砸了一点。我想在一个有点大的项目中改善可空性情况(让它更“迅速”)。这是我发现的。

首先,你应该打开CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION (-Wnullable-to-nonnull-conversion)

其次,关于

首先使用本地对象,该对象的属性未指定可为空, 因此它可以用作期望非空方法的合法参数。

这很难闻,我创建了一个名为 NONNUL_CAST() 的宏。以下是如何实现它的示例:

#define NONNUL_CAST(__var) ({ NSCAssert(__var, @"Variable is nil");\
    (__typeof(*(__var))* _Nonnull)__var; })

在这里你可以看到 hacky __typeof(*(__var))* _Nonnull)__var,但还不错。如果__varA* _Nullable 类型,我们取消引用__var,所以现在它的类型只是A,在我们再次引用之后,但是_Nonnull,并得到非空__var 作为答案。我们当然会断言,以防万一出现问题。

第三,你必须为每个局部变量指定可空性,你应该把你所有的代码放在NS_ASSUME_NONNULL_BEGIN/END,像这样:

NS_ASSUME_NONNULL_BEGIN 
<your_code_goes_here>
NS_ASSUME_NONNULL_END`

您将代码的每一行(导入除外)放在.h.m 文件中。这将假设您的方法、返回类型和属性的所有参数都是非空的。如果你想让它可以为空,把它放在那里。

所以,一切都完成了,现在呢? 以下是典型用法示例:

- (Atype*)makeAtypeWithBtype:(nullable BType*)btype {
  Atype* _Nullable a = [btype makeAtype];
  if (a) {
    // All good here.
    return NONNUL_CAST(a);
  } else {
    // a appeared as nil. Some fallback is needed.
    [self reportError];
    return [AtypeFactory makeDeafult];
  }
}

现在您有了更强大的可空性情况。可能看起来不太好看,但它是客观的,所以没什么可抱怨的。

【讨论】:

  • 很好的答案!我遇到了同样的问题,我完全忘记了取消引用和重新引用。
【解决方案2】:

并非每个警告都有意义。有时这是编译器的一个缺点。例如,此代码不需要警告。

- (nullable id)transformedValue:(nullable id)value {
    id result = value != nil ? UIImageJPEGRepresentation(value, 1.0) : nil;
    return result;
}

我们正在检查它是否为空!我们还能做什么?为什么要创建一个额外的指针?

所以,我们这样做:

- (nullable id)transformedValue:(nullable id)value {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wnullable-to-nonnull-conversion"
    id result = value != nil ? UIImageJPEGRepresentation(value, 1.0) : nil;
#pragma clang diagnostic pop
    return result;
}

为什么这是正确的答案?

首先,比编译器更聪明是可以的。你不想仅仅因为一个虚假的警告就开始破坏你的代码。

此解决方案指定要禁止显示的确切警告消息,并且仅将其禁止显示一行。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-17
    • 2013-09-04
    • 2013-03-20
    • 1970-01-01
    相关资源
    最近更新 更多