这是一个如何优雅地规避课堂上的可空性的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
?
我注意到有些人甚至将价值观记录nonnull
在现实中nullable
.那有意义吗?
我应该大胆地简单地添加到NS_ASSUME_NONNULL_BEGIN
任何地方,而不是真正确保价值观nonnull
吗?
这里似乎有两个问题:
如何确保在我的单例sharedInstance
方法中生成并由该方法返回的值在运行时实际上不是零?
我如何满足nullabilty注释,编译器警告和Swift桥接系统,我正在返回一个nonnull
指针?
nonnull
在某些时候,每个API合同都会分解为人工合同.编译器可以帮助确保,例如,您不能接受nullable
调用的结果并从返回类型为nonnull
... 的方法返回它,但某处通常是原始调用,其返回类型nonnull
仅仅是因为编写的程序员它说,"我保证永远不会归零,交叉我的心等等"
如果您熟悉Swift,这与Implicitly Unwrapped Optionals的情况类似 - 当您"知道"某个值不能为零时使用这些,但无法向编译器证明该知识,因为该知识是外部的源代码(例如来自故事板或包资源的东西).
那这里的情况-你"知道" init
将永远不会返回零,或者是因为你写/有源有问题的初始化或因为它只是NSObject
的init
被记录到return self
没有做任何事情.调用该初始化程序失败的唯一情况是因为前面的alloc
调用失败(因此您正在调用一个nil
始终返回的方法nil
).如果alloc
返回nil,你已经陷入了困境,你的过程对于这个世界来说并不长 - 这不是设计API的失败案例.
(可空性注释通常用于描述API的预期用途,而不是更极端的边缘和极端情况.如果API调用仅因为通用错误而失败,则将其注释为可空是没有意义的;同样,如果API仅失败在可以通过非空参数注释排除的输入上,可以假设返回值是非空的.)
所以,长话短说:是的,只需把NS_ASSUME_NONNULL
你的标题放在一边,sharedInstance
按原样运送你的实现.
nonnull
可空这不是这里的情况,但假设你有一个注释为的值nullable
,但你知道(或"知道")它永远不会是零并想要从你nonnull
注释的方法中返回它.而且你遇到了编译器警告的情况.
有一个语法 - 只需将值转换为预期的返回类型,注释和所有:
return (NSWhatever *_Nonnull)whatever;
在你的情况下,这应该没有必要-因为你处理的id
和instancetype
特殊类型的编译器是更宽容有关的为空的转换,可能会不发出警告的开始.