苹果公司的文件如下:
您可以一起使用if和let来处理可能丢失的值。这些值表示为可选项。可选值要么包含值,要么包含nil,表示该值缺失。在值的类型后面写一个问号(?),将该值标记为可选值。
为什么要使用可选值?
苹果公司的文件如下:
您可以一起使用if和let来处理可能丢失的值。这些值表示为可选项。可选值要么包含值,要么包含nil,表示该值缺失。在值的类型后面写一个问号(?),将该值标记为可选值。
为什么要使用可选值?
当前回答
我做了一个简短的回答,总结了上面的大部分内容,以清除我作为初学者脑海中的不确定性:
与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中,可选类型既可以有值,也可以没有值。可选选项是通过附加?对任何类型:
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开始) 更多可选的提示和技巧
嗯…
? (可选)表示您的变量可能包含nil值,而!(unwrapper)表示在运行时使用变量(试图从中获取值)时,变量必须具有内存(或值)。
主要的区别是,当可选对象为nil时,可选链接会优雅地失败,而当可选对象为nil时,强制展开会触发一个运行时错误。
为了反映可选链接可以在nil值上调用的事实,可选链接调用的结果总是一个可选值,即使您正在查询的属性、方法或下标返回一个非可选值。你可以使用这个可选的返回值来检查可选的链接调用是否成功(返回的可选值包含一个值),或者由于链中的nil值而不成功(返回的可选值为nil)。
具体来说,可选链接调用的结果与预期返回值的类型相同,但包装在可选类型中。一个属性,通常返回一个Int将返回一个Int?当通过可选链接访问时。
var defaultNil : Int? // declared variable with default nil value
println(defaultNil) >> nil
var canBeNil : Int? = 4
println(canBeNil) >> optional(4)
canBeNil = nil
println(canBeNil) >> nil
println(canBeNil!) >> // Here nil optional variable is being unwrapped using ! mark (symbol), that will show runtime error. Because a nil optional is being tried to get value using unwrapper
var canNotBeNil : Int! = 4
print(canNotBeNil) >> 4
var cantBeNil : Int = 4
cantBeNil = nil // can't do this as it's not optional and show a compile time error
以下是苹果开发者委员会提供的基本教程:可选链接
让我们以NSError为例,如果没有返回错误,你会想让返回Nil是可选的。如果没有错误,给它赋值是没有意义的。
var error: NSError? = nil
这也允许您拥有一个默认值。因此,如果函数没有传递任何东西,你可以为方法设置一个默认值
func doesntEnterNumber(x: Int? = 5) -> Bool {
if (x == 5){
return true
} else {
return false
}
}
在objective C中,没有值的变量等于“nil”(也可以使用与0和false相同的“nil”值),因此可以在条件语句中使用变量(有值的变量与“TRUE”相同,而那些没有值的变量等于“false”)。
Swift通过提供“可选值”来提供类型安全。即,它可以防止因分配不同类型的变量而形成错误。
所以在Swift中,只有条件语句可以提供布尔值。
var hw = "Hello World"
这里,虽然'hw'是一个字符串,但它不能像objective - C那样在if语句中使用。
//This is an error
if hw
{..}
为此,它需要被创建为,
var nhw : String? = "Hello World"
//This is correct
if nhw
{..}
在Swift中,可选类型既可以有值,也可以没有值。可选选项是通过附加?对任何类型:
var name: String?
您可以参考这个链接来深入了解:https://medium.com/@agoiabeladeyemi/ opoptions -in-swift-2b141f12f870