Swift Optionals - 语法逻辑

时间:2016-01-25 22:15:24

标签: swift optional chaining

看看这个条件的例子我很困惑。 这是代码和我的解释

var animal = Animal(name: "Lenny", species: "lemur", tailLength: 12)
animal = Animal(name: "Gilbert", species: "Gorilla", tailLength: nil )

if let tailLength = animal.tail?.length {
    print("\(animal.name)'s tail is \(tailLength) long")
} else {
    print("\(animal.name) doesn't have a tail.")
}

我们有一个变量" Animal"。并非所有动物都有尾巴,因此一些tailLength值将返回零。

对我来说,一个可选项是带选项的东西 - 在这种情况下它可以是Int或nil(在上面的Gilbert的情况下)。

现在,当我们要打开它时,我们正在使用链接来检查它是否会返回nil。如果它返回一个Int,我们返回它。如果它返回nil,我们转到else语句。

为什么是语法 -

if let tailLength = animal.tail?.length

而不是

if let tailLength = animal.taillength?

if let tailLength = animal.tail.length?

注意:编程新手。我知道答案可能需要一些其他语言和常用语法的必备知识。

1 个答案:

答案 0 :(得分:2)

让我们明白这一点。

首先是一些先决条件......

选配

期权并不一定意味着期权。相反,"可选"只是意味着价值可能存在,也可能不存在,因此它是可选的"。将Optionals视为恰到好处的容器,以便它们只能容纳一种特定类型。例如,专门为保留Int而创建的Optional可能永远不会拥有String,反之亦然。根据这个定义,Optionals听起来与变量和常量完全相同,但要注意的最大区别是,与常规变量和常量不同,Optionals的容器可能包含"根本没有值",AKA {{1或者它们可能包含指定类型的值。没有其他选择。

任何时候都可以选择使用期权,因为nil更好地表示没有任何价值,因为nil更好地表示没有任何价值。比如说,-1表示整数。

例如,

class Contact {

    var name: String
    var phoneNumber: String?
    var emailAddress: String?

    init(name: String, number: String?, emailAddress: String?) {
        self.name = name
        self.phoneNumber = number
        self.emailAddress = emailAddress
    }

}

这是一个非常简化的版本,可用于表示联系人应用中的条目。为简单起见,我们将使用String来表示每个属性。类型名称后面的问号表示我们不希望这些属性属于String类型,而是String?或"可选字符串"。该应用程序要求用户输入其名称,但用户可能会或可能不会选择提供电话号码或电子邮件地址,因此相应的属性将声明为可选字符串。例如,如果用户提供电子邮件地址,则emailAddress属性将保留表示地址的String。如果用户未提供地址,则emailAddress属性不会包含String,而是nil,或者没有值。

在幕后,Optional只是一个包含两种情况的枚举:None,而some则包含声明为optional的类型的包装值。如果您不熟悉Swift枚举或这让您感到困惑,请忽略此段落。理解Optionals并不重要。

期权总是只有两个选项。 nil或其声明类型的值。

展开选项

现在假设我们想在某个时候访问Optional。我们无法引用它,因为它可能是nil并且我们的计算不能正常运行。例如,如果我们想要访问用户的电子邮件地址,我们可能会尝试类似

的内容
let user = Contact(name: "Matt", number: nil, emailAddress: nil)
sendEmail(user.emailAddress)

假设sendEmail函数需要String表示地址作为参数,Swift将报告错误而不让代码按原样编译。问题是我们无法保证实际上会有String传递sendEmailsendEmail需要String,但我们尝试将String?传递给let user = Contact(name: "Matt", number: nil, emailAddress: nil) sendEmail(user.emailAddress!) 这些类型不同。如果我们知道该值确实存在,我们可以在可选值后立即添加感叹号。所以,继续我们的例子,

emailAddress

这里,感叹号告诉Swift我们确定String?属性中会有一个值,因此Swift继续将String转换为emailAddress值。如果在运行时发现nil评估为emailAddress,我们将收到致命错误。当然,在这种情况下,我们会在运行时遇到致命错误,因为nil确实包含Contact

然而,很多时候,特别是在我们的nil示例中,我们无法保证存在emailAddress。现在介绍Optional Binding ......

可选绑定

有些时候,我们根本不知道某个值是否存在,但是想要将该值用于某些值,我们可以使用Optional Binding。

假设我们想要向用户发送电子邮件,如果他们提供了他们的地址。好吧,我们需要确保值不是if user.emailAddress != nil { let address = user.emailAddress! sendEmail(address) } else { promptForEmailAddress() } ,如果它不是,则将其分配给常量,如果是,则执行一些解决方法。这是一个非常有效的方法:

nil

在这里,我们正在检查以确保地址不是nil。如果是,我们会要求用户将其提供给我们。如果地址确实存在(如果它不是if),我们将通过强制用感叹号展开它来将其分配给常量。我们可以这样做,因为如果true语句解析为user.emailAddress,我们绝对可以肯定nil不会是sendEmail。然后满足address因为String类型为String?而非if let address = user.emailAddress { sendEmail(address) } else { promptForEmailAddress() } ,因为我们先将其解包。

但是因为这是Swift中的一种常见模式,所以该语言定义了一种简化的方法(可选绑定)。以下代码段与上面的代码段完全相同。

if

使用可选绑定,您可以将赋值操作与nil语句结合使用。如果等于运算符(赋值运算符)右侧的任何内容求值为else,则调用address子句并且永远不会创建String常量 。但是,如果equals运算符右侧的表达式确实包含一个值,在本例中为address,则创建String常量,并使用emailAddress值进行初始化。存储在address中。因此,String的类型为String?而不是nil,因为如果创建了常量,我们就知道它不是emailAddress

可选链接

现在,有时您需要访问位于长链对象末尾的属性。例如,假设String不是Email,而是一个特殊的domainName对象,它拥有自己的几个属性,包括声明为' String&的visitWebAddress(user.emailAddress.domainName) #39;

visitWebAddress

如果其中一个值(user,emailAddress或domainName)被声明为Optional,那么这将不是一个安全的调用,Swift会对我们大喊大叫,因为我们想象的String函数需要{{1} },但得到了String?。实际上,这甚至不是有效的代码,因为?之后我们应该有一个emailAddress。这里发生了什么?好吧,请记住emailAddress被声明为String?或"可选字符串"。任何时候我们想要在已经可选的值上使用点语法访问属性,我们必须使用感叹号(强制可选unwrapped并返回必须存在的值)或问号。 无论我们选择!还是?,该符号都必须紧跟在声明为“可选”的属性后面。

visitWebAddress(user.emailAddress?.domainName)

此语句在语法上是正确的。问号告诉Swift我们意识到emailAddress可能是nil。如果是,则整个语句user.emailAddress?.domainName的值将为nil无论emailAddress实际上是niluser.emailAddress?.domainName的结果类型都是String?,而不是String因此,斯威夫特仍会对我们大喊大叫,因为visitWebAddress期待String,但得到String?

因此,我们可以结合使用Optional Binding和Optional Chaining来为这个问题创建一个简单而优雅的解决方案:

if let domain = user.emailAddress?.domainName {
    visitWebAddress(domain)
}

请记住,首先评估等于运算符右侧的表达式。如前所述,如果未指定电子邮件地址,user.emailAddress?.domainName将评估为String? nil。如果是这种情况并且表达式求值为nil,则不会创建任何常量,并且我们已完成if语句。否则,该值被绑定(因此"可选绑定")到创建的常量domain(将具有类型String),我们可以在if子句中使用它。

作为旁注,当值为nil时,可选链接有时会导致无法执行任何操作。例如,

textField?.text = "Hello"

此处,如果textField评估为nil(例如,如果尚未加载UI),则会跳过整行,并且不会采取任何操作。

现在提出您的确切问题,

代码分析

根据您发布的代码示例,这可能会发生。 Animal类可能至少看起来像这样:

class Animal {

    let name: String
    let species: String
    let tail: Tail?

    init(name: String, species: String, tailLength: Int?) {
        self.name = name
        self.species = species
        if let lengthOfTail = tailLength {
            self.tail = Tail(length: lengthOfTail)
        }
    }

}

现在,您可能想知道所有这些提及Tail对象的内容是什么,所以这可能也存在于幕后的某个地方,至少看起来像这样:

class Tail {

    let length: Int

    init(length: Int) {
        self.length = length
    }

}

(现在回过头看Animal应该更有意义。)

在这里,我们有一个Tail对象,其中一个属性名为length,类型为Int(不是可选的)。但是,Animal类包含一个名为' tail'的属性。它的类型为Tail?,或者"可选的尾部"。这意味着Animal可能有也可能没有tail,但如果确实如此,则保证length类型为Int

所以这里......

if let tailLength = animal.tail?.length {
    print("\(animal.name)'s tail is \(tailLength) long")
} else {
    print("\(animal.name) doesn't have a tail.")
}

... ?紧跟在tail之后,因为它是可选的属性。声明animal.tail?.length的类型为Int?,如果它是nil,因为tailnil,则调用else子句。否则,将创建一个常量tailLength,并将尾长作为Int绑定到它。

请注意,在Animal初始值设定项中,Int?只需tailLength,但在幕后检查它是否为nil并且仅在Tail没有创建具有指定长度的tail对象。否则,Animal上的nil属性保留为nil,因为{{1}}是未初始化的可选变量或常量的默认值。

希望在Swift中清除关于Optionals的所有问题。 Swift Language Guide有一些很好的内容,所以如果需要,请查看。