在 Swift 中检查 nil 的非可选值



在 Swift 中,很少见,但最终可能会得到具有 nil 值的非可选类型的值。正如在回答这个问题时所解释的,这可能是由桥接到 Swift 的糟糕的 Objective-C 代码引起的:

- (NSObject * _Nonnull)someObject {
    return nil;
}

或者通过糟糕的 Swift 代码:

class C {}
let x: C? = nil
let y: C = unsafeBitCast(x, to: C.self)

在实践中,我在 MessageUI 中使用 MFMailComposeViewController API 遇到了这个问题。下面创建一个非可选MFMailComposeViewController,但如果用户尚未在 Mail 中设置电子邮件帐户,则以下代码会崩溃并显示EXC_BAD_ACCESS

let mailComposeViewController = MFMailComposeViewController()
print("(mailComposeViewController)")

调试器如下所示显示mailComposeViewController的值:

mailComposeViewController = (MFMailComposeViewController) 0x0000000000000000

我在这里有几个问题:

  1. 我注意到 unsafeBitCast(_:to:) 的文档说它"破坏了 Swift 类型系统的保证",但是 Swift 文档中是否有一个地方可以解释这些保证可以被打破,以及如何/何时被打破?
  2. 检查这种情况的惯用方法是什么?编译器不会让我检查是否mailComposeViewController == nil,因为它不是可选的。

即使是 Apple 的 API 有时也会为在 API 中未标记为 Optional 的类型返回 nil。解决方案是分配给可选。

例如,有一段时间traitCollectionDidChange返回了一个 UITraitCollection,即使它实际上可能是nil的。您无法检查它nil,因为 Swift 不会让您检查非 Optional for nil

解决方法是立即将返回的值分配给UITraitCollection?,并检查值是否nil 。这种事情也应该适用于你的用例(尽管你的邮件示例不是一个用例,因为你从一开始就做错了)。

对于实际的 Swift 指针类型,可能有更好的方法可以做到这一点,但一种选择可能是只查看地址:

func isNull(_ obj: AnyObject) -> Bool {
    let address = unsafeBitCast(obj, to: Int.self)
    return address == 0x0
}

Int被记录为机器字大小。

我有同样的问题,但我使用了Apple文档中的建议。

从文档中:

在显示邮件撰写视图控制器之前,请始终调用 canSendMail() 方法,用于查看当前设备是否配置为 发送电子邮件。如果用户的设备未设置为交付 电子邮件,您可以通知用户或简单地禁用电子邮件调度 应用程序中的功能。您不应尝试使用此 接口,如果 canSendMail() 方法返回 false。

if !MFMailComposeViewController.canSendMail() {
    print("Mail services are not available")
    return
}

考虑使用unsafeBitCast(_:to:)

主要问题是,在这种方法中,我们将指针投射到 Swift 中的值,而无需验证该指针是否可以符合我们期望的类型,从我的角度来看,这是非常危险的,因为它会产生崩溃。

另一种方法是声明一个类似于下面的函数:

@inline(never) func isNil<T>(value: T?) -> Bool {
    value == nil
}

如果没有@inline(never),您将在发布版本中面临风险,因为编译器可以选择内联函数体,然后看到value == nil理论上永远不会为真,因此也删除了检查,使生产代码的行为就像您永远不会编写用于检查 nils 的代码一样。

相关内容

  • 没有找到相关文章

最新更新