【问题标题】:Can we ensure nullability of `+ (nonnull instancetype)sharedInstance;`?我们能否确保`+ (nonnull instancetype)sharedInstance;` 的可空性?
【发布时间】:2015-12-10 06:28:31
【问题描述】:

这是一个关于如何优雅地规避initNSObject 类中的可空性的问题。

所以这是一个经典的objective-c实现:

+ (instancetype)sharedInstance
{
    static dispatch_once_t onceToken;
    static id sharedInstance;
    dispatch_once(&onceToken, ^
    {
        sharedInstance = [[self alloc] init];
    });
    return sharedInstance;
}

但现在我想将其声明为nonnull,如果可能的话:

+ (nonnull instancetype)sharedInstance;

不幸的是,init 返回一个 nullable instancetype 值。我应该在调用init 之后添加NSAssert 或其他内容吗?

我注意到有些人甚至document nonnull values as being in reality nullable。这有意义吗?

我是否应该大胆地在所有地方简单地添加 NS_ASSUME_NONNULL_BEGIN,而不真正确保值是 nonnull

【问题讨论】:

  • init 被定义为可为空的,因为对于无法初始化自身的对象而言,简单地返回 nil 而不是有效对象是合法的。 NSObject 不会那样做(在实践中),如果你的类直接继承自 NSObject,你可以保证 sharedInstance 不会是 nil,方法是永远不会从你的 init 覆盖返回 nil .
  • 附加评论:nullablenonnull 是定义合同的属性,而不是现实。它们让编译器知道会发生什么,因此编译器可以坚持按照合同进行正确处理。这对 Swift 来说显然很重要,它的既定目标是最大限度地减少程序员的错误。担心内存不足等情况不是您的工作,当合同另有规定时,这可能会导致引用为 nil
  • 您为什么关心可空性?当然,Objective-C 中的所有变量 nullable 甚至带有前缀 nonnull。它是与 Swift 互操作性的发明。 Objective-C 中只有一件有用的事情:在方法中标记一些参数,它们应该有值,nil 可能会导致崩溃或意外结果。
  • @Cy-4AH CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION
  • @Cy-4AH 可能想说的是,在 Objective-C 中使用 nonnullnullable 装饰方法并没有真正的优势。这是因为 Objective-C 动态调度所有消息,其中包括一个空检查。在 Objective-C 中处理空值并利用消息到 nil 模式是很常见的。仅与 Swift 互操作才有意义。因此,如果没有与 Swift 的互操作,就不要使用它。过去 30 年我们没有这样做。

标签: objective-c nullable init non-nullable objective-c-nullability


【解决方案1】:

这里似乎有两个问题:

  1. 如何确保在我的单例 sharedInstance 方法中生成并由该方法返回的值在运行时实际上不为零?

  2. 如何满足可空性注释、编译器警告和 Swift 桥接系统的要求,即我返回了 nonnull 指针?

确保/执行nonnull

在某些时候,每个 API 合同都会分解为人工合同。编译器可以帮助确保,例如,您不能获取 nullable 调用的结果并从返回类型为 nonnull 的方法中返回它... nonnull 只是因为写它的程序员说,“我保证永远不会返回null,我的心,等等。”

如果你熟悉 Swift,这类似于使用隐式解包 Optionals 的情况——当你“知道”一个值不能为 nil 时使用它们,但不能向编译器证明该知识,因为该知识位于源代码之外(例如,来自故事板或捆绑资源的内容)。

这就是这里的情况——你“知道”init 永远不会返回 nil,要么是因为你写了/有相关初始化程序的源代码,要么因为它只是 NSObjectinit 记录在 @ 987654332@ 什么都不做。对该初始化程序的调用将失败的唯一情况是因为前面的alloc 调用失败(因此您在nil 上调用一个方法,它总是返回nil)。如果 alloc 返回 nil,那么您已经在 dire straits 中,并且您的流程对于这个世界来说并不长——这不是围绕设计 API 的失败案例。

(Nullability 注释通常用于描述 API 的预期用途,而不是更极端的边缘和角落情况。如果 API 调用仅因为 universal error 而失败,则将其注释为可为 null 是没有意义的;同样,如果API 仅在可以通过非空参数注释排除的输入上失败,返回值可以假定为非空。)

所以,长话短说:是的,只需将 NS_ASSUME_NONNULL 放在您的标题周围,然后按原样发布您的 sharedInstance 实现。

返回一个 nonnull 可空

这里不是这样,但是假设你有一个被注释为nullable 的值,但是你知道(或“知道”)它永远不会是 nil 并且想要从你的 nonnull-annotated 方法中返回它.而且您会在尝试时收到编译器警告。

有一个语法——只需将值转换为预期的返回类型、注释和所有内容:

return (NSWhatever *_Nonnull)whatever;

在您的情况下,这不应该是必需的 - 因为您正在处理 idinstancetype 特殊类型,编译器对可空性转换更加宽容,并且可能不会在开始时发出警告。

【讨论】:

  • 使用可空性注解的另一个好处是,当超类为 NSObject 或超级初始化器返回不可为空时,我们现在可以摆脱 if (self) 签入初始化器。
猜你喜欢
  • 2020-01-16
  • 1970-01-01
  • 2018-07-28
  • 2017-10-17
  • 2011-03-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-28
相关资源
最近更新 更多