在 Swift 中检查 nil 的非可选值的最佳方法
Best way to check non-optional values for nil in Swift
在Swift中,这种情况很少见,但有可能以具有 nil 值的非可选类型的值结束。正如 的回答中所解释的,这可能是由于错误的 Objective-C 代码桥接到 Swift:
- (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
我有几个问题:
- 我注意到
unsafeBitCast(_:to:)
的 documentation 说的是 "breaks the guarantees of the Swift type system," 但是 Swift 文档中是否有一个地方解释了这些保证可以被破坏,并且 how/when?
- 检查这种情况的最佳、最惯用的方法是什么?编译器不会让我检查是否
mailComposeViewController == nil
因为它不是可选的。
我有同样的问题,但我使用了 Apple 文档中的建议。
来自文档:
Before presenting the mail compose view controller, always call the
the canSendMail() method to see if the current device is configured to
send email. If the user’s device is not set up for the delivery of
email, you can notify the user or simply disable the email dispatch
features in your application. You should not attempt to use this
interface if the canSendMail() method returns false.
if !MFMailComposeViewController.canSendMail() {
print("Mail services are not available")
return
}
考虑使用 unsafeBitCast(_:to:)
。
主要问题是,在这个方法中,我们将一个指针从 Swift 转换为一个值,而没有任何验证该指针是否符合我们期望的类型,从我的角度来看,这是相当危险,因为它会产生崩溃。
对于实际的 Swift 指针类型,可能有更好的方法来执行此操作,但一种选择可能是只查看地址:
func isNull(_ obj: AnyObject) -> Bool {
let address = unsafeBitCast(obj, to: Int.self)
return address == 0x0
}
(Int
记录为机器字大小。)
即使是 Apple 的 API 有时 return nil
对于未在 API 中标记为可选的类型。解决方案是分配给一个 Optional.
例如,有一段时间 traitCollectionDidChange
return 编辑了一个 UITraitCollection,尽管它实际上可能是 nil
。您无法检查 nil
,因为 Swift 不允许您检查 nil
.
的非可选项
解决方法是立即将 returned 值分配给 UITraitCollection?
并检查 that 以获得 nil
。无论您的用例是什么,这种事情都应该适用(尽管您的邮件示例不是用例,因为您从一开始就做错了)。
在Swift中,这种情况很少见,但有可能以具有 nil 值的非可选类型的值结束。正如
- (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
我有几个问题:
- 我注意到
unsafeBitCast(_:to:)
的 documentation 说的是 "breaks the guarantees of the Swift type system," 但是 Swift 文档中是否有一个地方解释了这些保证可以被破坏,并且 how/when? - 检查这种情况的最佳、最惯用的方法是什么?编译器不会让我检查是否
mailComposeViewController == nil
因为它不是可选的。
我有同样的问题,但我使用了 Apple 文档中的建议。
来自文档:
Before presenting the mail compose view controller, always call the the canSendMail() method to see if the current device is configured to send email. If the user’s device is not set up for the delivery of email, you can notify the user or simply disable the email dispatch features in your application. You should not attempt to use this interface if the canSendMail() method returns false.
if !MFMailComposeViewController.canSendMail() {
print("Mail services are not available")
return
}
考虑使用 unsafeBitCast(_:to:)
。
主要问题是,在这个方法中,我们将一个指针从 Swift 转换为一个值,而没有任何验证该指针是否符合我们期望的类型,从我的角度来看,这是相当危险,因为它会产生崩溃。
对于实际的 Swift 指针类型,可能有更好的方法来执行此操作,但一种选择可能是只查看地址:
func isNull(_ obj: AnyObject) -> Bool {
let address = unsafeBitCast(obj, to: Int.self)
return address == 0x0
}
(Int
记录为机器字大小。)
即使是 Apple 的 API 有时 return nil
对于未在 API 中标记为可选的类型。解决方案是分配给一个 Optional.
例如,有一段时间 traitCollectionDidChange
return 编辑了一个 UITraitCollection,尽管它实际上可能是 nil
。您无法检查 nil
,因为 Swift 不允许您检查 nil
.
解决方法是立即将 returned 值分配给 UITraitCollection?
并检查 that 以获得 nil
。无论您的用例是什么,这种事情都应该适用(尽管您的邮件示例不是用例,因为您从一开始就做错了)。