6

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

所以这是一个经典的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值。我应该NSAssert在调用后添加一个或其他内容init吗?

我注意到有些人甚至将价值观记录nonnull为现实nullable。那有意义吗?

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

4

1 回答 1

7

这里似乎有两个问题:

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

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

确保/执行nonnull

在某些时候,每个 API 合约都会分解为人工合约。编译器可以帮助确保,例如,您不能获取nullable调用的结果并从返回类型为nonnull... 的方法中返回它,但在某个地方通常有一个原始调用,其返回类型nonnull仅仅是因为编写它说,“我保证永远不会返回 null,越过我的心,等等。”

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

这就是这里的情况——你“知道”它init永远不会返回 nil,要么是因为你写了/有相关初始化程序的源代码,要么是因为它只是NSObject'sinit记录下来而return self没有做任何事情。对该初始化程序的调用将失败的唯一情况是因为前面的alloc调用失败(因此您正在调用 on 的方法nil,该方法总是返回nil)。如果alloc返回 nil,那么您已经陷入困境,并且您的流程对于这个世界来说并不长——这不是围绕设计 API 的失败案例。

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

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

返回一个可为nonnull空的

这里不是这种情况,但假设您有一个被注释为 的值nullable,但您知道(或“知道”)它永远不能为 nil 并希望从您的nonnull-annotated 方法中返回它。而且您处于尝试时收到编译器警告的情况。

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

return (NSWhatever *_Nonnull)whatever;

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

于 2017-02-20T21:44:26.840 回答