我们可以确保 `+ (nonnull instancetype)sharedInstance;` 的可空性吗?

Can we ensure nullability of `+ (nonnull instancetype)sharedInstance;`?

这是一个关于如何在 NSObject class.

中优雅地规避 init 的可空性的问题

所以这是一个 classic 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 returns 一个 nullable instancetype 值。我应该在调用 init 之后添加一个 NSAssert 还是什么?

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

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

这里好像有两个问题:

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

  2. 我如何满足可空性注释、编译器警告和 Swift 桥接我正在 return 一个 nonnull 指针的系统?

确保/执行nonnull

在某些时候,每个 API 合同都会分解为人类合同。编译器可以帮助确保,例如,您不能从 return 类型为 nonnull 的方法中获取 nullable 调用的结果和 return 它。 . 但通常某处有一个 return 类型为 nonnull 的原始调用,仅仅是因为编写它的程序员说,"I promise to never return null, cross my heart, etc."

如果您熟悉 Swift,这类似于隐式解包可选的情况——当您 "know" 一个值不能为 nil,但不能证明时,您可以使用它们该知识对编译器来说是因为该知识在源代码之外(例如,来自故事板或捆绑资源的东西)。

这就是这里的情况——你 "know" init 永远不会 return nil,要么是因为你写了/有相关初始化程序的源代码,要么是因为它只是 NSObjectinit 记录到 return self 没有做任何事情。对该初始化程序的调用失败的唯一情况是因为前面的 alloc 调用失败(因此你在 nil 上调用一个方法,它总是 returns nil).如果 alloc 是 return 零,你已经在 dire straits 并且你的过程对这个世界来说并不长 - 这不是设计 API 的失败案例左右。

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

所以,长话短说:是的,只需将 NS_ASSUME_NONNULL 放在 header 周围,然后按原样运送 sharedInstance 实施。

返回一个 nonnull 可为 null 的

这里不是这种情况,但假设您有一个注释为 nullable 的值,但您知道(或 "know")它永远不会为 nil 并且想要 return 它来自你的 nonnull-注释方法。而且您遇到了尝试时收到编译器警告的情况。

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

return (NSWhatever *_Nonnull)whatever;

在你的情况下,这不应该是必需的——因为你正在处理 idinstancetype 特殊类型,编译器对可空性转换更宽容,可能不会发出警告开始于.