Swift中检查nil的非可选值

5

在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,但是如果用户没有在邮件中设置电子邮件帐户,则以下代码会崩溃,并显示EXC_BAD_ACCESS

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

调试器显示mailComposeViewController的值如下:
mailComposeViewController = (MFMailComposeViewController) 0x0000000000000000

我有几个问题:

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

1
邮件编辑器的显而易见的解决方案是首先检查是否可以发送电子邮件。 - rmaddy
4个回答

6
有时候即使API没有标注为可选项,苹果的API也会返回nil。 解决方法是将其分配给一个可选项。
例如,有一段时间,即使可能为niltraitCollectionDidChange仍返回UITraitCollection。 您无法检查nil,因为Swift不允许您检查非可选项的nil
解决方法是立即将返回值分配给UITraitCollection?并检查是否为nil。 对于您的用例,这种事情应该也适用(尽管您的邮件示例不是用例,因为您从一开始就做错了)。

我遇到了这样的情况,我该怎么办?写信给苹果申报一个 bug 吗?在我的情况下,这显然像是有人忘记加问号了 :) - FrizzTheSnail

1

也许使用实际的Swift指针类型有更好的方法,但其中一种选择可能是只需查看地址:

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

(Int 被记录为机器字大小。)


这不是完全通用的:我不确定如何处理值类型,其中包括自动桥接为[Any]NSArray - jscs

0
另一种方法是声明一个类似于这样的函数:
@inline(never) func isNil<T>(value: T?) -> Bool {
    value == nil
}

没有使用@inline(never),在发布版本中存在风险,因为编译器可以选择内联函数体,然后发现value == nil理论上永远不可能为真,因此也会删除检查,使生产代码的行为就像您从未编写过针对nil的检查代码一样。


0

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

从文档中可以看到:

在呈现邮件撰写视图控制器之前,始终调用 canSendMail() 方法以查看当前设备是否配置为发送电子邮件。如果用户的设备未设置为传递电子邮件,则可以通知用户或仅禁用应用程序中的电子邮件发送功能。如果 canSendMail() 方法返回 false,则不应尝试使用此接口。

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

考虑使用unsafeBitCast(_:to:)

主要问题在于,在此方法中,我们将指针从Swift强制转换为值,而没有对该指针进行任何验证,以确定它是否符合我们所期望的类型。在我看来,这是非常危险的,因为它会导致崩溃。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接