苹果公司的文件如下:

您可以一起使用if和let来处理可能丢失的值。这些值表示为可选项。可选值要么包含值,要么包含nil,表示该值缺失。在值的类型后面写一个问号(?),将该值标记为可选值。

为什么要使用可选值?


当前回答

在Swift中,可选类型既可以有值,也可以没有值。可选选项是通过附加?对任何类型:

var name: String? = "Bertie"

可选项(以及泛型)是Swift中最难理解的概念之一。由于它们的编写和使用方式,很容易对它们有错误的理解。将上面的可选操作与创建普通字符串进行比较:

var name: String = "Bertie" // No "?" after String

从语法上看,它看起来像一个可选的字符串,非常类似于普通的字符串。它不是。一个可选的字符串不是一个打开了一些“可选”设置的字符串。它不是字符串的特殊变种。String和可选String是完全不同的类型。

这里有一件最重要的事情需要知道:可选对象是一种容器。可选String是可能包含String的容器。可选Int型是一个容器,它可能包含一个Int型。可以把可选的东西看作是一种包裹。在你打开它之前(或者在可选语言中“打开”),你不会知道它是否包含一些东西或什么都没有。

通过在任何Swift文件中输入“Optional”并⌘-点击它,您可以看到可选项是如何在Swift标准库中实现的。下面是定义的重要部分:

enum Optional<Wrapped> {
    case none
    case some(Wrapped)
}

Optional只是一个枚举,可以是两种情况之一:.none或.some。如果它是.some,则有一个相关的值,在上面的例子中,该值将是字符串“Hello”。可选选项使用泛型为关联值指定类型。可选String的类型不是String,而是optional,或者更准确地说optional <String>。

Swift在可选选项上所做的一切都是魔法,让阅读和编写代码更加流畅。不幸的是,这掩盖了它实际工作的方式。我稍后会讲一些技巧。

注意:我将经常谈论可选变量,但也可以创建可选常数。我用它们的类型标记所有变量,以便更容易理解所创建的类型类型,但您不必在自己的代码中这样做。


如何创建可选项

要创建可选对象,请添加?在希望换行的类型之后。任何类型都可以是可选的,甚至是您自己的自定义类型。类型和?之间不能有空格。

var name: String? = "Bob" // Create an optional String that contains "Bob"
var peter: Person? = Person() // An optional "Person" (custom type)

// A class with a String and an optional String property
class Car {
var modelName: String // must exist
var internalName: String? // may or may not exist
}

使用可选的

你可以将一个可选对象与nil进行比较,看看它是否有值:

var name: String? = "Bob"
name = nil // Set name to nil, the absence of a value
if name != nil {
    print("There is a name")
}
if name == nil { // Could also use an "else"
    print("Name has no value")
}

这有点让人困惑。它意味着可选的东西不是这个就是那个。要么是nil,要么是Bob。这是不对的,可选选项不会转化为其他东西。将它与nil进行比较是一种使代码更易于阅读的技巧。如果一个可选值等于nil,这仅仅意味着枚举当前被设置为。none。


只有可选选项可以为nil

如果你试图将一个非可选变量设置为nil,你会得到一个错误。

var red: String = "Red"
red = nil // error: nil cannot be assigned to type 'String'

Another way of looking at optionals is as a complement to normal Swift variables. They are a counterpart to a variable which is guaranteed to have a value. Swift is a careful language that hates ambiguity. Most variables are define as non-optionals, but sometimes this isn't possible. For example, imagine a view controller which loads an image either from a cache or from the network. It may or may not have that image at the time the view controller is created. There's no way to guarantee the value for the image variable. In this case you would have to make it optional. It starts as nil and when the image is retrieved, the optional gets a value.

使用可选对象可以揭示程序员的意图。与Objective-C中任何对象都可以为nil相比,Swift需要你清楚什么时候一个值可以丢失,什么时候它可以保证存在。


要使用可选选项,需要“展开”它

可选的字符串不能用来代替实际的字符串。要在可选对象中使用被包装的值,必须将其展开。展开可选对象的最简单方法是添加一个!在可选名称之后。这就是所谓的“力展开”。它返回可选类型中的值(作为原始类型),但如果可选类型为nil,则会导致运行时崩溃。在展开之前,您应该确保有一个值。

var name: String? = "Bob"
let unwrappedName: String = name!
print("Unwrapped name: \(unwrappedName)")

name = nil
let nilName: String = name! // Runtime crash. Unexpected nil.

检查和使用一个可选的

因为你应该总是检查nil在展开和使用一个可选的,这是一个常见的模式:

var mealPreference: String? = "Vegetarian"
if mealPreference != nil {
    let unwrappedMealPreference: String = mealPreference!
    print("Meal: \(unwrappedMealPreference)") // or do something useful
}

在此模式中,您将检查一个值是否存在,然后当您确定它存在时,您将强制将其展开为一个临时常数以供使用。因为这是一件很常见的事情,Swift提供了一个使用“if let”的快捷方式。这被称为“可选绑定”。

var mealPreference: String? = "Vegetarian"
if let unwrappedMealPreference: String = mealPreference {
    print("Meal: \(unwrappedMealPreference)") 
}

这将创建一个临时常数(如果用var替换let则为变量),其作用域仅在if的大括号内。因为必须使用像“unwrappedMealPreference”或“realMealPreference”这样的名称是一种负担,Swift允许您重用原始变量名,在括号范围内创建一个临时变量名

var mealPreference: String? = "Vegetarian"
if let mealPreference: String = mealPreference {
    print("Meal: \(mealPreference)") // separate from the other mealPreference
}

下面是演示使用不同变量的代码:

var mealPreference: String? = "Vegetarian"
if var mealPreference: String = mealPreference {
    print("Meal: \(mealPreference)") // mealPreference is a String, not a String?
    mealPreference = "Beef" // No effect on original
}
// This is the original mealPreference
print("Meal: \(mealPreference)") // Prints "Meal: Optional("Vegetarian")"

可选绑定的工作原理是检查可选对象是否等于nil。如果没有,它将可选选项解包装到提供的常量中并执行该块。在Xcode 8.3及以后版本(Swift 3.1)中,尝试打印这样的可选选项会导致无用的警告。使用可选参数的debugDescription使其静音:

print("\(mealPreference.debugDescription)")

什么是可选的?

可选项有两个用例:

可能会失败的事情(我一直期待着一些东西,但我什么都没有得到) 现在什么都不是,但以后可能会成为的事情(反之亦然)

一些具体的例子:

A property which can be there or not there, like middleName or spouse in a Person class A method which can return a value or nothing, like searching for a match in an array A method which can return either a result or get an error and return nothing, like trying to read a file's contents (which normally returns the file's data) but the file doesn't exist Delegate properties, which don't always have to be set and are generally set after initialization For weak properties in classes. The thing they point to can be set to nil at any time A large resource that might have to be released to reclaim memory When you need a way to know when a value has been set (data not yet loaded > the data) instead of using a separate dataLoaded Boolean

Optionals don't exist in Objective-C but there is an equivalent concept, returning nil. Methods that can return an object can return nil instead. This is taken to mean "the absence of a valid object" and is often used to say that something went wrong. It only works with Objective-C objects, not with primitives or basic C-types (enums, structs). Objective-C often had specialized types to represent the absence of these values (NSNotFound which is really NSIntegerMax, kCLLocationCoordinate2DInvalid to represent an invalid coordinate, -1 or some negative value are also used). The coder has to know about these special values so they must be documented and learned for each case. If a method can't take nil as a parameter, this has to be documented. In Objective-C, nil was a pointer just as all objects were defined as pointers, but nil pointed to a specific (zero) address. In Swift, nil is a literal which means the absence of a certain type.


与nil的比较

你过去可以使用任意可选类型作为布尔值:

let leatherTrim: CarExtras? = nil
if leatherTrim {
    price = price + 1000
}

在最近的Swift版本中,你必须使用leatherTrim != nil。为什么会这样?问题是布尔值可以被包装在可选对象中。如果你有这样的布尔值:

var ambiguous: Boolean? = false

它有两种“假”,一种是没有值,另一种是有值但值为假。Swift讨厌模糊性,所以现在你必须检查一个可选对象是否为nil。

您可能想知道可选布尔值的意义是什么?与其他可选选项一样,.none状态可以指示该值是未知的。在网络呼叫的另一端可能有一些东西需要一些时间进行轮询。可选布尔值也被称为“三值布尔值”


斯威夫特的技巧

Swift使用了一些技巧来允许可选选项工作。考虑这三行看起来普通的可选代码;

var religiousAffiliation: String? = "Rastafarian"
religiousAffiliation = nil
if religiousAffiliation != nil { ... }

这些行都不能编译。

第一行使用String字面值设置了一个可选String,这是两种不同的类型。即使这是一个字符串,类型也是不同的 第二行将可选String设置为nil,这是两种不同的类型 第三行将可选字符串与nil进行比较,这是两种不同的类型

我将详细介绍允许这些行工作的可选选项的一些实现细节。


创建一个可选

使用?创建一个可选的是语法糖,由Swift编译器启用。如果你想从长远来看,你可以创建一个像这样的可选选项:

var name: Optional<String> = Optional("Bob")

这将调用Optional的第一个初始化式public init(_ some: Wrapped),它从括号内使用的类型推断出Optional的关联类型。

创建和设置可选对象的更长的方法:

var serialNumber:String? = Optional.none
serialNumber = Optional.some("1234")
print("\(serialNumber.debugDescription)")

将一个可选参数设置为nil

你可以创建一个没有初始值的可选对象,也可以创建一个初始值为nil的可选对象(两者具有相同的结果)。

var name: String?
var name: String? = nil

允许可选项等于nil是由协议expressiblebynlitereral(以前称为n文盲可转换)启用的。可选对象是用optional的第二个初始化式public init(nlitereral:())创建的。文档说,你不应该使用expressiblebynilleral的任何东西,除了可选的,因为这会改变你的代码中nil的含义,但它是可能做到的:

class Clint: ExpressibleByNilLiteral {
    var name: String?
    required init(nilLiteral: ()) {
        name = "The Man with No Name"
    }
}

let clint: Clint = nil // Would normally give an error
print("\(clint.name)")

同样的协议允许您将一个已经创建的可选项设置为nil。虽然不建议这样做,但你可以直接使用nil文字初始化式:

var name: Optional<String> = Optional(nilLiteral: ())

将可选对象与nil进行比较

可选选项定义了两个特殊的“==”和“!”="操作符,你可以在Optional定义中看到。第一个==允许你检查任意可选值是否等于nil。如果相关联的类型相同,则两个不同的可选选项设置为.none将始终相等。当你比较nil时,Swift在幕后创建了一个可选的相同关联类型,设置为.none,然后使用它进行比较。

// How Swift actually compares to nil
var tuxedoRequired: String? = nil
let temp: Optional<String> = Optional.none
if tuxedoRequired == temp { // equivalent to if tuxedoRequired == nil
    print("tuxedoRequired is nil")
}

第二个==操作符允许您比较两个可选选项。两者必须是相同的类型,并且该类型需要符合Equatable(该协议允许使用常规的“==”操作符进行比较)。Swift(推测)将这两个值展开并直接进行比较。它还处理一个或两个可选项都为.none的情况。注意比较和nil字面量之间的区别。

此外,它允许你将任何Equatable类型与可选的封装类型进行比较:

let numberToFind: Int = 23
let numberFromString: Int? = Int("23") // Optional(23)
if numberToFind == numberFromString {
    print("It's a match!") // Prints "It's a match!"
}

在幕后,Swift在比较之前将非可选选项包装为可选选项。它也适用于字面量(if 23 == numberFromString {)

我说过有两个==操作符,但实际上还有第三个,允许你把nil放在比较的左边

if nil == name { ... }

命名可选

There is no Swift convention for naming optional types differently from non-optional types. People avoid adding something to the name to show that it's an optional (like "optionalMiddleName", or "possibleNumberAsString") and let the declaration show that it's an optional type. This gets difficult when you want to name something to hold the value from an optional. The name "middleName" implies that it's a String type, so when you extract the String value from it, you can often end up with names like "actualMiddleName" or "unwrappedMiddleName" or "realMiddleName". Use optional binding and reuse the variable name to get around this.


官方定义

摘自Swift编程语言的“基础知识”:

Swift also introduces optional types, which handle the absence of a value. Optionals say either “there is a value, and it equals x” or “there isn’t a value at all”. Optionals are similar to using nil with pointers in Objective-C, but they work for any type, not just classes. Optionals are safer and more expressive than nil pointers in Objective-C and are at the heart of many of Swift’s most powerful features. Optionals are an example of the fact that Swift is a type safe language. Swift helps you to be clear about the types of values your code can work with. If part of your code expects a String, type safety prevents you from passing it an Int by mistake. This enables you to catch and fix errors as early as possible in the development process.


最后,这是一首1899年写的关于可选方案的诗:

昨天在楼梯上 我遇到了一个不在场的男人 他今天又不在那里 我希望,我希望他离开 Antigonish


更多资源:

Swift编程指南 Swift(中)可选选项 WWDC会议402“Swift简介”(14:15开始) 更多可选的提示和技巧

其他回答

我做了一个简短的回答,总结了上面的大部分内容,以清除我作为初学者脑海中的不确定性:

与Objective-C相反,在Swift中没有变量可以包含nil,所以增加了Optional变量类型(变量以"?"结尾):

    var aString = nil //error

最大的区别是可选变量不直接存储值(像普通的Obj-C变量一样),它们包含两种状态:“有值”或“有nil”:

    var aString: String? = "Hello, World!"
    aString = nil //correct, now it contains the state "has nil"

也就是说,你可以在不同的情况下检查这些变量:

if let myString = aString? {
     println(myString)
}
else { 
     println("It's nil") // this will print in our case
}

通过使用"!"后缀,您还可以访问包含在其中的值,前提是这些值存在。(即它不是nil):

let aString: String? = "Hello, World!"
// var anotherString: String = aString //error
var anotherString: String = aString!

println(anotherString) //it will print "Hello, World!"

这就是为什么您需要使用“?”和“!”,而不是默认情况下全部使用它们。(这是我最大的困惑)

我也同意上面的回答:可选类型不能用作布尔类型。

这很简单。可选(在Swift中)意味着变量/常数可以为空。你可以看到Kotlin语言实现了同样的事情,但从未将其称为“可选”。例如:

var lol: Laugh? = nil

等价于Kotlin:

var lol: Laugh? = null

在Java中是这样的:

@Nullable Laugh lol = null;

在第一个例子中,如果您没有在对象类型前面使用?符号,那么就会出现错误。因为问号意味着变量/常数可以为空,因此被称为可选的。

当我开始学习Swift时,很难意识到为什么是可选的。

让我们这样想。 让我们考虑一个类Person,它有两个属性名和公司。

class Person: NSObject {
    
    var name : String //Person must have a value so its no marked as optional
    var companyName : String? ///Company is optional as a person can be unemployed that is nil value is possible
    
    init(name:String,company:String?) {
        
        self.name = name
        self.companyName = company
        
    }
}

现在让我们创建Person的几个对象

var tom:Person = Person.init(name: "Tom", company: "Apple")//posible
var bob:Person = Person.init(name: "Bob", company:nil) // also Possible because company is marked as optional so we can give Nil

但是我们不能将Nil传递给name

var personWithNoName:Person = Person.init(name: nil, company: nil)

现在我们来谈谈为什么我们用optional? 让我们考虑这样一种情况,我们想在公司名称后面加上Inc,比如apple将是apple Inc。我们需要在公司名称和打印后附加Inc。

print(tom.companyName+" Inc") ///Error saying optional is not unwrapped.
print(tom.companyName!+" Inc") ///Error Gone..we have forcefully unwrap it which is wrong approach..Will look in Next line
print(bob.companyName!+" Inc") ///Crash!!!because bob has no company and nil can be unwrapped.

现在让我们来研究一下为什么会出现“可选”。

if let companyString:String = bob.companyName{///Compiler safely unwrap company if not nil.If nil,no unwrap.
    
    print(companyString+" Inc") //Will never executed and no crash!!!
}

咱们把鲍勃换成汤姆吧

if let companyString:String = tom.companyName{///Compiler safely unwrap company if not nil.If nil,no unwrap.
    
    print(companyString+" Inc") //Will executed and no crash!!!
}

和祝贺!我们是否妥善处理可选事宜?

实现要点是

如果一个变量可能为nil,我们将其标记为可选 如果我们想在代码的某个地方使用这个变量,编译器将 提醒你们一下,我们需要检查一下我们是否正确地处理了这个变量 如果它包含nil。

谢谢你!快乐的编码

从https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/OptionalChaining.html:

可选链接是一个查询和调用属性、方法和当前可能为nil的可选对象的下标的过程。如果可选选项包含值,则属性、方法或下标调用成功;如果可选值为nil,则属性、方法或下标调用将返回nil。多个查询可以链接在一起,如果链中的任何链接为nil,整个链就会优雅地失败。

要更深入地了解,请阅读上面的链接。

下面是Swift中等价的可选声明:

var middleName: String?

这个声明创建了一个名为midlename的String类型变量。String变量类型后面的问号(?)表示middleame变量可以包含String或nil值。看过这段代码的人都知道middleame可以是nil。这是自我记录!

如果你没有为一个可选的常量或变量指定一个初始值(如上所示),这个值会自动为你设置为nil。如果你愿意,你可以显式地将初始值设置为nil:

var middleName: String? = nil

有关可选选项的更多详细信息,请阅读下面的链接

http://www.iphonelife.com/blog/31369/swift-101-working-swifts-new-optional-values