简体   繁体   English

在Swift中检查非可选值是否为nil的最佳方法

[英]Best way to check non-optional values for nil in Swift

In Swift, it's rare but possible to end up with a value of a non-optional type that has a nil value. 在Swift中,这种情况很少见,但最终可能会得到一个非零类型的值,该值具有nil值。 As explained in answers to this question , this can be caused by bad Objective-C code bridged to Swift: 如对这个问题的回答所述 ,这可能是由桥接到Swift的不良Objective-C代码引起的:

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

Or by bad Swift code: 或通过错误的Swift代码:

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

In practice, I've run into this with the MFMailComposeViewController API in MessageUI . 在实践中,我已经使用MessageUIMFMailComposeViewController API进行了此操作。 The following creates a non-optional MFMailComposeViewController , but if the user has not set up an email account in Mail, the following code crashes with EXC_BAD_ACCESS : 以下代码创建了一个非可选的MFMailComposeViewController ,但是,如果用户尚未在Mail中设置电子邮件帐户,则以下代码将因EXC_BAD_ACCESS崩溃:

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

The debugger shows the value of mailComposeViewController like this: 调试器如下所示显示mailComposeViewController的值:

mailComposeViewController = (MFMailComposeViewController) 0x0000000000000000

I have a couple of questions here: 我在这里有几个问题:

  1. I note that the documentation for unsafeBitCast(_:to:) says it "breaks the guarantees of the Swift type system," but is there a place in Swift documentation that explains that these guarantees can be broken, and how/when? 我注意到unsafeBitCast(_:to:)文档说它“破坏了Swift类型系统的保证”,但是Swift文档中有一个地方可以解释这些保证可以被打破,以及如何/何时?
  2. What's the best, most idiomatic way to check for this case? 检查这种情况的最好,最惯用的方法是什么? The compiler won't let me check whether mailComposeViewController == nil since it's not optional. 编译器不会让我检查mailComposeViewController == nil因为它不是可选的。

Even Apple's APIs sometimes return nil for a type that is not marked in the API as Optional. 甚至Apple的API有时也会针对API中未标记为Optional的类型返回nil The solution is to assign to an Optional. 解决方案是将其分配给Optional。

For example, for a while traitCollectionDidChange returned a UITraitCollection even though it could in fact be nil . 例如,有一段时间traitCollectionDidChange返回了一个UITraitCollection,即使它实际上可能是nil You couldn't check it for nil because Swift won't let you check a non-Optional for nil . 您无法将其检查为nil因为Swift不会让您检查nil的non-Optional。

The workaround was to assign the returned value immediately to a UITraitCollection? 解决方法是立即将返回值分配给UITraitCollection? and check that for nil . 检查nil That sort of thing should work for whatever your use case is as well (though your mail example is not a use case, because you're doing it wrong from the get-go). 无论您的用例是什么,这种事情都应该起作用(尽管您的邮件示例不是用例,因为您一开始就做错了)。

I have same issue but I use suggestion from the Apple docs. 我有同样的问题,但我使用了苹果文档中的建议。

From the documentation: 从文档中:

Before presenting the mail compose view controller, always call the the canSendMail() method to see if the current device is configured to send email. 在呈现邮件撰写视图控制器之前,请始终调用canSendMail()方法以查看当前设备是否配置为发送电子邮件。 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. 如果canSendMail()方法返回false,则不应尝试使用此接口。

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

Considering usage of the unsafeBitCast(_:to:) . 考虑使用unsafeBitCast(_:to:)

The main problem is that in this method we cast a pointer to a value from Swift without any validation if that pointer can conform to the type we are expecting, and from my point of view it is quite dangerous because it will produce crash. 主要问题在于,在此方法中,如果该指针可以符合我们期望的类型,则无需任何验证就可以将指针从Swift转换为值,从我的角度来看,这很危险,因为它会导致崩溃。

There is probably a better way to do this with actual Swift pointer types, but one option might be to just peek at the address: 使用实际的Swift指针类型可能有更好的方法,但是一种选择可能是仅查看地址:

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

( Int is documented as machine word sized.) Int被记录为机器字大小。)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM