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

时间:2015-12-10 06:28:32

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

这是一个关于如何优雅地规避init课程中NSObject的可空性的问题。

所以这是一个经典的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或其他内容吗?

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

我应该大胆一点,只需在任何地方添加NS_ASSUME_NONNULL_BEGIN,而不是真正确保价值为nonnull

1 个答案:

答案 0 :(得分:5)

这里似乎有两个问题:

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

  2. 如何满足nullabilty注释,编译器警告和Swift桥接系统我正在返回nonnull指针?

  3. 确保/执行nonnull

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

    如果您熟悉Swift,这与Implicitly Unwrapped Optionals的情况类似 - 当您“知道”某个值不能为零时使用这些,但无法向编译器证明该知识,因为该知识在源代码的外部(例如来自故事板或捆绑资源的东西)。

    这就是这里的情况 - 你“知道”init将永远不会返回nil,因为你为有问题的初始化程序编写/拥有源代码,或者因为它只是NSObject的{​​{1}}在没有做任何事情的情况下记录到init。调用该初始值设定项失败的唯一情况是因为前面的return self调用失败(因此您在alloc上调用方法,该方法始终返回nil)。如果nil返回nil,那么您已经在dire straits,并且您的流程对于这个世界而言并不长 - 这不是设计API的失败案例。

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

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

    返回sharedInstance可以为空的

    这不是这种情况,但假设你有一个注释为nonnull的值,但是你知道(或“知道”)它永远不会是零并想要从{{1}返回它 - 注释方法。并且您处于编译器警告状态的情况下。

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

    nullable

    在你的情况下,这不应该是需要的 - 因为你正在处理nonnullreturn (NSWhatever *_Nonnull)whatever; 特殊类型,编译器对于可空性转换更加宽容,并且可能不会警告开始