- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
这是一个关于如何优雅地规避 NSObject
类中的 init
可空性的问题。
这是一个经典的 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
?
最佳答案
这里好像有两个问题:
如何确保在我的单例 sharedInstance
方法中产生并由该方法返回的值在运行时实际上不是 nil?
我如何满足可空性注释、编译器警告和 Swift 桥接的系统要求我返回一个 nonnull
指针?
nonnull
在某些时候,每个 API 契约(Contract)都会分解为人工契约(Contract)。编译器可以帮助确保,例如,您不能获取 nullable
调用的结果并从返回类型为 nonnull
的方法返回它...但是某处通常有一个返回类型为 nonnull
的原始调用,这仅仅是因为编写它的程序员说,“我保证永远不会返回 null,我很伤心,等等。”
如果您熟悉 Swift,这类似于隐式解包选项的情况——当您“知道”一个值不能为 nil 时使用它们,但无法向编译器证明该知识,因为该知识在源代码之外(例如,来自 Storyboard或捆绑资源的东西)。
这就是这里的情况——你“知道”init
永远不会返回 nil,要么是因为你写了/有相关初始化程序的源代码,要么是因为它只是 NSObject
的 init
被记录为 return self
而不做任何事情。对该初始化程序的调用失败的唯一情况是因为前面的 alloc
调用失败(因此您在 nil
上调用一个方法,它总是返回 无
)。如果 alloc
返回 nil,那么你已经在 dire straits 中了并且您的流程对于这个世界来说并不长 — 这不是围绕设计 API 的失败案例。
(可空性注释通常用于描述 API 的预期用途,而不是更极端的极端情况。如果 API 调用仅因为 universal error 而失败,则将其注释为可空性没有意义;同样,如果API 仅在可以通过非空参数注释排除的输入上失败,返回值可以假定为非空。)
所以,长话短说:是的,只需将 NS_ASSUME_NONNULL
放在您的标题周围,然后按原样发送您的 sharedInstance
实现。
nonnull
这里不是这种情况,但假设您有一个注释为 nullable
的值,但您知道(或“知道”)它永远不会为 nil 并且想从您的 nonnull
- 注释方法。并且您遇到了尝试时收到编译器警告的情况。
有一个语法——只需将值转换为预期的返回类型、注释和所有:
return (NSWhatever *_Nonnull)whatever;
在你的情况下,这不应该是必需的——因为你正在处理 id
和 instancetype
特殊类型,编译器对可空性转换更宽容并且可能会赢'警告开始。
关于objective-c - 我们可以确保 `+ (nonnull instancetype)sharedInstance;` 的可空性吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34195149/
在此代码中,当我键入 GIDSignIn.sharedInstance 时,出于某种原因 sharedInstance 不是函数,这意味着我无法访问 clientID。我在任何地方都找不到解决方案。
在我的代码中,我初始化了两个单例。我有 var session = AVAudioSession.sharedInstance 和 var audioSession = AVAudioSession.
我正在将项目转换为 SDK。我需要将几个实例方法转换为类方法。我收到有关使用“self”的编译器警告。警告是“使用 Class 表达式初始化 Store* 的不兼容指针类型。此 Store 类是单例
假设我有以下单例: @interface ABCSingleton: NSObject @property (nonatomic, copy) NSString *name; @property (n
所有单例函数都是两次调用。“Prova”函数和定时器的选择器是两次调用。 class Timer { static let sharedInstanceTimer = Timer()
我正在创建一个音频应用程序,我希望音频在屏幕关闭时继续播放。(它在屏幕打开时在后台运行良好。) 在 xcode 上启用了后台模式(我在 Info.plist 中设置了“音频”值。)并且我实现了“AVA
我不久前在我的 iOS 项目中集成了 Digits by Fabric 以通过电话号码对用户进行身份验证,直到最近我的应用程序开始崩溃时一切正常,原因是我们在项目中初始化 Digits 的以下代码行:
我有一个小型 iOS 应用程序 (xcode8 ios10),我需要用户使用 facebook 或 google 登录。我已经完成了 facebook 部分,并且效果很好。然后今天我添加了谷歌登录支持
具有以下单例: class Colors { static let sharedInstance = Colors() private init() {} let mainCo
sharedInstance 究竟是什么?我是说有什么用? 目前我在 2 个不同文件之间进行通信时遇到了一些问题。 这是我的问题: 我有 1 个文件调用 A.h/A.m 和另一个文件调用 B.h/B.
我们正在使用键值观察来查看音频输出路由是否通过以下代码进行了更改: AVAudioSession *audioSession = [AVAudioSession sharedInstance]; [a
我正在尝试在我的 iOS 应用程序中使用 TwitterKit 框架(在 Swift 中)。但是,当我使用 Twitter.sharedInstance().APIClient 时,会出现错误,提示“
我已经阅读了谷歌文档来寻求答案,我也查看了一些类似的帖子,但这些都没有帮助我解决我的问题。我正在尝试将 UIButton 设置为 google 登录的触发器。由于某种原因,它压碎了。我当然会添加我编写
我想从一些代码中展示一个 MFMailComposerViewController,这些代码深藏在可通过共享实例访问的实用程序类中。 当我尝试时 [self presentViewController
使用以下代码,我得到了输出音量,但它确实不一致 - 有时它给出相同的值,有时它是一个落后的音量变化,尽管系统音量实际上是正确变化的。 有什么办法让它每次都输出正确的值? func viewDidLoa
我有一个要使用 XCTest 测试的类,这个类看起来像这样: public class MyClass: NSObject { func method() { // Do s
我正在将一些自 iOS 9.0 以来已弃用的 UIAlertViews 更新为 UIAlertViewControllers。 使用 UIAlertView,可以从任何正在执行的代码中抛出警报——即使
我正在创建一个快速框架。其中一个类如下所示。 import Foundation @objc public class classA: NSObject { public overr
我遇到了类似于 this one 的问题。基本上每次我的应用启动时,我都必须使用我的 Google 帐户登录。 然后,我有这个属性: var isGoogleSessionOpen: Bool {
我在 sharedInstance return Static.instance! 行发生以下崩溃: EXC_BREAKPOINT 0x0000000100da42d8 崩溃发生在没有调试断点的 Ad
我是一名优秀的程序员,十分优秀!