Swift中的可选项出现问题

4

我是一个 Swift 新手,一直在尝试让可选值起作用,但却总是进入死循环。我进行了大量的谷歌搜索,阅读了苹果文档等等,但仍然无法解决以下 ViewController 中的这个(简化的)代码段:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    if (segue.identifier == "segueToWebView") {
        // check what we're passing around
        print(sender!.urlString) // Optional("http://www.bbc.co.uk")

        // do some url validation etc
        var destUrl:String
        if sender!.urlString == nil{
            destUrl = "http://www.google.com"
        } else {
            destUrl = sender!.urlString // *** Error here ***
        }

        let targetWebViewController:GDWebViewController = segue.destinationViewController as! GDWebViewController
        targetWebViewController.loadURLWithString(destUrl)
        targetWebViewController.showsToolbar = true
        targetWebViewController.title = sender!.busName // this works correctly too (so isn't that a String?)
    }
}

错误信息是Cannot assign value of type 'String?!' to type 'String',标记的那行代码出了问题。我已经尝试过不同的解包方法,但还是无法解决问题。
更多代码
发送者是一个自定义的UIButton子类,具有以下结构:
import UIKit

class TestButton: UIButton {
    var urlString: String?
    var busName: String?
}

我将变量设置为可选的,因为我不知道按钮是否具有这些属性。

当按钮被点击时调用的方法是:

func tapTap(sender: TestButton) {
    print(sender.busName, sender.urlString) // Optional("My Button") Optional("http://www.bbc.co.uk")
    self.performSegueWithIdentifier("segueToWebView", sender: sender)
}

我知道Swift中的可选项对于像我这样的新手来说是一个常见的绊脚石,虽然有很多文档可以参考,但我仍无法理解,所以如果能提供任何帮助将不胜感激。


你代码中的第三行 print(sender!.urlString) 不应该编译通过,因为 AnyObject 没有成员 urlString - Nikolai Ruhe
var destUrl:String 替换为 var destUrl:String?,然后尝试。 - Mohammad Sadiq Shaikh
谢谢大家 - 我现在正在火车上,所以会尽快查看你们的建议并回复。 - James
@NikolaiRuhe:它编译成功是因为你可以向AnyObject发送任意(已知)消息,类似于Objective-C中的id - Martin R
1
@MartinR 我刚刚读了你(链接的)帖子——我之前不知道这个。感谢你提供的信息性描述! - Nikolai Ruhe
3个回答

10

有三个级别的可选项:

  • sender: AnyObject? 参数是一个可选项。
  • AnyObject发送任意消息类似于可选链接,并返回一个隐式展开的可选项(参见Swift AnyObject的奇怪行为)。
  • var urlString: String? 属性是一个可选项。

要解决前两个可选项,使用可选绑定/类型转换到具体的按钮类:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    if (segue.identifier == "segueToWebView") {
        if let button = sender as? TestButton {
           // The type of `button`  is `TestButton`
           // ...
        }
    }
}

现在。
var destUrl: String
if button.urlString == nil {
    destUrl = "http://www.google.com"
} else {
    destUrl = button.urlString!
}

可以编译通过,但最好使用nil-coalescing运算符来处理:

let destUrl = button.urlString ?? "http://www.google.com"

请注意,现在没有强制解包操作符!了!


感谢您的出色回答!我意识到“可选项内部的可选项”存在问题,但如果没有您的帮助,我永远不会找到解决方案。我将您的第二段代码放在了if let中,似乎可以正常工作。非常感谢您的帮助。 - James

3
你只需要像这样做:
var destUrl:String
if let durl = sender!.urlString {
   destUrl = durl
} else {
   destUrl = "http://www.google.com"
}

你的代码问题在于destUrl是字符串,但sender!.urlString是一个可选项。因此你不能将它们之一分配给另一个变量。在分配之前必须解包可选项。可以使用强制解包(使用!)或者使用if let进行解包操作。我建议使用if let,但这取决于你的选择。

3
你正在手动测试字符串是否为nil,但之后你忘记了对其进行解包:
var destUrl:String
if sender!.urlString == nil{
    destUrl = "http://www.google.com"
} else {
    destUrl = sender!.urlString!
}

(看到添加的感叹号了吗。)

但是像这样解包字符串实际上更容易:

let destUrl = sender!.urlString ?? "http://www.google.com"
?? 运算符可以在一行代码中测试 nil 值,解包或返回右侧的值。

谢谢你关于 ?? 运算符的提示 - 在未来肯定会很方便。 - James

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