Swift中的可选值是什么?

Tim*_*len 258 optional swift

来自Apple的文档:

您可以使用iflet一起处理可能缺少的值.这些值表示为选项.可选值包含值或包含nil以指示缺少值.?在值的类型后面写一个问号()以将值标记为可选.

为什么要使用可选值?

nev*_*ing 509

Swift中的可选项是一种可以保存值或不保存值的类型.通过将a附加?到任何类型来编写选项:

var name: String? = "Bertie"
Run Code Online (Sandbox Code Playgroud)

Optionals(以及Generics)是最难理解的Swift概念之一.由于它们的编写和使用方式,很容易弄清楚它们是什么.将上面的可选项与创建普通String进行比较:

var name: String = "Bertie" // No "?" after String
Run Code Online (Sandbox Code Playgroud)

从语法看起来,可选的String与普通的String非常相似.不是.可选的String不是打开某些"可选"设置的String.它不是一种特殊的String.String和可选String是完全不同的类型.

这是最重要的事情:可选是一种容器.可选的String是一个可能包含String的容器.可选的Int是一个可能包含Int的容器.将可选项视为一种包裹.在打开它之前(或用选项语言"展开"),你不会知道它是否包含任何东西.

通过在任何Swift文件中键入"Optional"并⌘-单击它,您可以在Swift标准库中查看选项的实现方式.这是定义的重要部分:

enum Optional<Wrapped> {
    case none
    case some(Wrapped)
}
Run Code Online (Sandbox Code Playgroud)

可选是一个enum可以是两种情况之一:.none.some.如果是.some,则存在一个相关值,在上面的例子中,它将是String"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
}
Run Code Online (Sandbox Code Playgroud)

使用选项

您可以比较可选项以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")
}
Run Code Online (Sandbox Code Playgroud)

这有点令人困惑.这意味着可选的是一件事或另一件事.它要么是零,要么是"鲍勃".事实并非如此,可选项不会转换为其他内容.将它与nil进行比较是制作易于阅读的代码的一种技巧.如果可选等于nil,这只意味着枚举当前设置为.none.


只有期权可以是零

如果您尝试将非可选变量设置为nil,则会出现错误.

var red: String = "Red"
red = nil // error: nil cannot be assigned to type 'String'
Run Code Online (Sandbox Code Playgroud)

查看选项的另一种方法是作为普通Swift变量的补充.它们是保证具有值的变量的对应物.斯威夫特是一种讨厌模糊的语言.大多数变量被定义为非选项,但有时这是不可能的.例如,假设一个视图控制器从高速缓存或网络加载图像.在创建视图控制器时,它可能有也可能没有该图像.无法保证图像变量的值.在这种情况下,您必须使其成为可选项.它nil在检索图像时开始,可选的获取值.

使用可选项显示程序员的意图.与Objective-C相比,任何对象都可以为零,Swift需要您清楚何时可以丢失值以及何时保证存在.


要使用可选项,您可以"打开"它

可选String不能代替实际使用String.要在可选内部使用包装值,您必须打开它.解包可选项的最简单方法是!在可选名称后面添加一个.这被称为"力展开".它返回可选内部的值(作为原始类型)但如果是可选的nil,则会导致运行时崩溃.打开之前你应该确定它有价值.

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

name = nil
let nilName: String = name! // Runtime crash. Unexpected nil.
Run Code Online (Sandbox Code Playgroud)

检查并使用可选项

因为在打开和使用可选项之前应始终检查nil,这是一种常见模式:

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

在这种模式中,您检查是否存在值,然后当您确定它是时,您强制将其打包到临时常量中以供使用.因为这是一件很常见的事情,Swift提供了一个使用"if let"的快捷方式.这称为"可选绑定".

var mealPreference: String? = "Vegetarian"
if let unwrappedMealPreference: String = mealPreference {
    print("Meal: \(unwrappedMealPreference)") 
}
Run Code Online (Sandbox Code Playgroud)

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

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

这里有一些代码来演示使用不同的变量:

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")"
Run Code Online (Sandbox Code Playgroud)

可选绑定通过检查可选等于nil来工作.如果没有,则将可选内容解包为提供的常量并执行该块.在Xcode 8.3及更高版本(Swift 3.1)中,尝试打印这样的可选项会导致无用的警告.使用可选项将debugDescription其静音:

print("\(mealPreference.debugDescription)")
Run Code Online (Sandbox Code Playgroud)

什么是期权?

Optionals有两个用例:

  1. 事情可能会失败(我期待的东西,但我什么都没有)
  2. 现在什么都不是,但后来可能会出现问题(反之亦然)

一些具体的例子:

  • 可以存在或不存在的属性,类似于middleNamespousePerson类中
  • 一种可以返回值或不返回值的方法,例如在数组中搜索匹配项
  • 一种方法,可以返回结果或获取错误并且不返回任何内容,例如尝试读取文件的内容(通常返回文件的数据)但文件不存在
  • 委托属性,不一定要设置,通常在初始化后设置
  • 对于weak类中的属性.他们指出,可以设置为东西nil在任何时间
  • 可能必须释放的大型资源以回收内存
  • 当你需要一种方法来知道何时设置了一个值(数据尚未加载>数据)而不是使用单独的dataLoaded Boolean

Objective-C中不存在选项,但是有一个等价的概念,返回nil.可以返回对象的方法可以返回nil.这被认为是指"没有有效物体",并且常常被用来说出错了.它仅适用于Objective-C对象,而不适用于基元或基本C类型(枚举,结构).Objective-C通常有专门的类型来表示缺少这些值(NSNotFound实际上NSIntegerMax,kCLLocationCoordinate2DInvalid表示无效坐标,-1或者也使用某些负值).编码人员必须了解这些特殊值,因此必须记录并学习每种情况.如果方法不能nil作为参数,则必须记录.在Objective-C中,nil指针就像所有对象都被定义为指针一样,但是nil指向一个特定的(零)地址.在Swift中,nil是一个文字,意味着缺少某种类型.


比较 nil

您曾经能够使用任何可选的Boolean:

let leatherTrim: CarExtras? = nil
if leatherTrim {
    price = price + 1000
}
Run Code Online (Sandbox Code Playgroud)

在更新版本的Swift中你必须使用leatherTrim != nil.为什么是这样?问题是a Boolean可以包装在一个可选项中.如果你有Boolean这样的:

var ambiguous: Boolean? = false
Run Code Online (Sandbox Code Playgroud)

它有两种"假",一种没有价值,一种有价值,但价值却是false.斯威夫特讨厌模棱两可,所以现在你必须经常检查一个可选项nil.

您可能想知道可选项的重点Boolean是什么?与其他选项一样,.none州可以表明该值尚未知.网络呼叫的另一端可能有某些东西需要一些时间来轮询.可选的布尔也称为" 三值布尔 "


快速的技巧

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

var religiousAffiliation: String? = "Rastafarian"
religiousAffiliation = nil
if religiousAffiliation != nil { ... }
Run Code Online (Sandbox Code Playgroud)

这些行都不应该编译.

  • 第一行使用String文字设置可选的String,两种不同的类型.即使这是一种String类型是不同的
  • 第二行将可选String设置为nil,两种不同类型
  • 第三行将可选字符串与nil,两种不同类型进行比较

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


创建一个可选的

使用?创建可选的语法糖,由斯威夫特编译器启用.如果你想做很长的事,你可以创建一个像这样的可选项:

var name: Optional<String> = Optional("Bob")
Run Code Online (Sandbox Code Playgroud)

这调用了Optional第一个初始化程序,public init(_ some: Wrapped)它从括号中使用的类型推断出可选的关联类型.

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

var serialNumber:String? = Optional.none
serialNumber = Optional.some("1234")
print("\(serialNumber.debugDescription)")
Run Code Online (Sandbox Code Playgroud)

将可选项设置为 nil

您可以创建一个没有初始值的可选项,也可以创建一个初始值为nil(两者都具有相同结果)的可选项.

var name: String?
var name: String? = nil
Run Code Online (Sandbox Code Playgroud)

nil通过协议ExpressibleByNilLiteral(先前命名NilLiteralConvertible)启用允许选项等于.使用Optional第二个初始化程序创建可选项public init(nilLiteral: ()).文档说你不应该使用ExpressibleByNilLiteral除选项之外的任何东西,因为这会改变代码中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)")
Run Code Online (Sandbox Code Playgroud)

相同的协议允许您将已创建的可选项设置为nil.虽然不推荐,但您可以直接使用nil literal初始化程序:

var name: Optional<String> = Optional(nilLiteral: ())
Run Code Online (Sandbox Code Playgroud)

比较可选项 nil

Optionals定义了两个特殊的"=="和"!="运算符,您可以在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")
}
Run Code Online (Sandbox Code Playgroud)

第二个==运算符允许您比较两个选项.两者都必须是相同的类型,并且该类型需要符合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!"
}
Run Code Online (Sandbox Code Playgroud)

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

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

if nil == name { ... }
Run Code Online (Sandbox Code Playgroud)

命名选项

命名可选类型与非可选类型不同,没有Swift约定.人们避免在名称中添加内容以显示它是可选的(如"optionalMiddleName"或"possibleNumberAsString"),并让声明显示它是可选类型.当你想要命名某些东西以保存可选项中的值时,这会变得很困难.名称"middleName"意味着它是一个String类型,因此当您从中提取String值时,通常最终会得到"actualMiddleName"或"unwrappedMiddleName"或"realMiddleName"等名称.使用可选绑定并重用变量名来解决此问题.


官方定义

Swift编程语言中的"基础知识":

Swift还引入了可选类型,它们处理缺少值.Optionals说"有一个值,它等于x"或"根本没有值".Optionals类似于在Objective-C中使用带有指针的nil,但它们适用于任何类型,而不仅仅是类.Option-C中的可选项比nil指针更安全,更具表现力,是Swift最强大功能的核心.

可选项是Swift是一种类型安全语言这一事实​​的一个例子.Swift可以帮助您清楚代码可以使用的值的类型.如果您的部分代码需要String,则类型安全性会阻止您错误地将其传递给Int.这使您能够在开发过程中尽早捕获并修复错误.


最后,这是1899年关于选项的一首诗:

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

Antigonish


更多资源:

  • `if myString`不再编译.你需要`if myString!= nil`.请参阅[文档](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html#sapple_ref/doc/uid/TP40014097-CH5-XID_483). (19认同)
  • @KaanDedeoglu不幸的是Steve非常可选.他在这里,现在他不是. (5认同)
  • 最好和最清楚的解释?而且!我在网上找到了Swift.谢谢 (4认同)
  • 感谢您的解释,它比Apple自己的文档更清晰. (4认同)
  • [mateo](http://matteomanferdini.com/the-complete-guide-to-understanding-swift-optionals/)详细解释了Optionals,深入了解和简单的例子. (2认同)
  • 天哪,唯一帮助我正确理解选项的教程。出色的工作,我的朋友@nevanking (2认同)
  • stackoverflow 上有史以来最完整的答案。你真的是一个国王先生“nevan king”。 (2认同)

Joh*_*ato 16

让我们以a为例NSError,如果没有返回错误,你可以选择让它返回Nil.如果没有错误,为它赋值是没有意义的.

var error: NSError? = nil
Run Code Online (Sandbox Code Playgroud)

这也允许您拥有默认值.因此,如果函数未传递任何内容,则可以将方法设置为默认值

func doesntEnterNumber(x: Int? = 5) -> Bool {
    if (x == 5){
        return true
    } else {
        return false
    }
}
Run Code Online (Sandbox Code Playgroud)


ric*_*ter 12

你不能拥有一个指向nilSwift 的变量- 没有指针,也没有空指针.但是在API中,您通常希望能够指示特定类型的值或缺少值 - 例如,我的窗口是否有委托,如果是,那么它是谁?可选项是Swift的类型安全,内存安全的方法.


Teo*_*aru 11

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

与Objective-C相反,Swift中没有变量可以包含nil,因此添加了Optional变量类型(变量后缀为"?"):

    var aString = nil //error
Run Code Online (Sandbox Code Playgroud)

最大的区别是Optional变量不直接存储值(正常的Obj-C变量)它们包含两个状态:" 有一个值 "或" 有nil ":

    var aString: String? = "Hello, World!"
    aString = nil //correct, now it contains the state "has nil"
Run Code Online (Sandbox Code Playgroud)

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

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

通过使用"!" 后缀,您也可以访问包含在其中的值,只有存在这些值.(即它不是):

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

println(anotherString) //it will print "Hello, World!"
Run Code Online (Sandbox Code Playgroud)

这就是你需要使用"?"的原因.和"!" 并且默认情况下不使用所有这些.(这是我最大的困惑)

我也同意上面的答案:可选类型不能用作布尔值.


Gok*_*kul 7

在目标C中,没有值的变量等于'nil'(也可以使用与n和false相同的'nil'值),因此可以在条件语句中使用变量(具有值的变量与'TRUE'相同'和那些没有价值观的人等于'假').

Swift通过提供"可选值"来提供类型安全性.即它可以防止因分配不同类型的变量而形成的错误.

所以在Swift中,只能在条件语句中提供布尔值.

var hw = "Hello World"
Run Code Online (Sandbox Code Playgroud)

在这里,偶数'虽然'hw'是一个字符串,但它不能在if语句中使用,就像在Objective C中一样.

//This is an error

if hw

 {..}
Run Code Online (Sandbox Code Playgroud)

为此,它需要创建为,

var nhw : String? = "Hello World"

//This is correct

if nhw

 {..}
Run Code Online (Sandbox Code Playgroud)


Mar*_*ina 5

可选值允许您显示缺少值.有点像SQL中的NULL或Objective-C中的NSNull.我想这将是一个改进,因为即使是"原始"类型也可以使用它.

// Reimplement the Swift standard library's optional type
enum OptionalValue<T> {
    case None
    case Some(T)
}
var possibleInteger: OptionalValue<Int> = .None
possibleInteger = .Some(100)”
Run Code Online (Sandbox Code Playgroud)

摘录自:Apple Inc."The Swift Programming Language."iBooks.https://itun.es/gb/jEUH0.l

  • `nil`只是枚举常量`OptionalValue <T> .None`的句法糖(其中`T`是适合你使用`nil`的上下文的类型).`?`是`OptionalValue <T> .Some(T)`的快捷方式. (6认同)