什么是Swift中的可选值?

从苹果的文档 :

您可以使用iflet来处理可能丢失的值。 这些值表示为可选项。 一个可选值包含一个值或者包含nil来表示缺失值。 在值的types后面写一个问号( ? ),将该值标记为可选。

你为什么要使用可选值?

Swift中的一个可选项是一个可以保存值或不保存值的types。 选项是通过附加一个? 到任何types:

 var name: String? = "Bertie" 

可选项(与generics一起)是Swift最难以理解的概念之一。 由于它们是如何被写入和使用的,很容易弄错它们是什么。 比较可选的上面创build一个正常的string:

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

从语法看来,可选的String类似于普通的String。 不是。 一个可选的string不是一个打开一些“可选”设置的string。 这不是一个特殊的string。 一个String和一个可选的String是完全不同的types。

以下是最重要的知识:可选是一种容器。 可选的string是一个可能包含一个string的容器。 可选的Int是一个可能包含Int的容器。 把一个可选的想象成一种包裹。 在打开之前(或者用可选的语言“打开”),你不会知道它是否包含任何东西。

您可以在Swift标准库中看到如何实现可选项,方法是在任何Swift文件中input“Optional”,然后单击⌘。 这是定义的重要部分:

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

可选只是一个enum ,可以是以下两种情况之一: .none.some 。 如果有的.some ,有一个关联的值,在上面的例子中,将是String “Hello”。 可选使用generics将关联的值赋予一个types。 可选string的types不是String ,它是Optional ,或者更确切地说是Optional<String>

所有Swift用option都是魔术,使读写代码更加stream畅。 不幸的是,这掩盖了它实际工作的方式。 我稍后会去做一些技巧。

注意:我会讨论可选variables,但也可以创build可选常量。 我将所有variables标记为它们的types,以便更容易理解正在创build的typestypes,但不必在自己的代码中。


如何创build选项

要创build一个可选的,附加一个? 你想包装的types之后。 任何types都可以是可选的,甚至是你自己的自定义types 你不能有一个types和?

 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比较是制作易于阅读的代码的一个技巧。 如果可选等于零,这只是意味着枚举当前设置为.none


只有optionals可以是零

如果你尝试设置一个非可选variables为零,你会得到一个错误。

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

另一种查看可选项的方法是对普通Swiftvariables的补充。 它们是保证具有价值的variables的对应物。 斯威夫特是一个讨厌含糊不清的语言。 大多数variables被定义为非可选项,但有时这是不可能的。 例如,假设一个视图控制器从caching或networking中加载图像。 在创build视图控制器时,它可能有也可能没有这个图像。 没有办法保证图像variables的值。 在这种情况下,你将不得不使其可选。 它nil ,当图像被检索时,可选值得到一个值。

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


要使用可选项,请“解开”它

可选的String不能用于实际的String 。 要在可选内部使用包装值,必须将其解包。 打开可选的最简单的方法是添加一个! 之后的可选名称。 这就是所谓的“解放力量”。 它返回可选内部的值(作为原始types),但是如果可选nil ,则会导致运行时崩溃。 在展开之前,你应该确定这是有价值的。

 var name: String? = "Bob" let unwrappedName: String = name! print("Unwrapped name: \(unwrappedName)") name = nil let nilName: String = name! // Runtime crash. Unexpected 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)") } 

这将创build一个临时常量(或者,如果您将letreplace为var ,则该variables的范围仅位于if的括号内)。 因为必须使用“unwrappedMealPreference”或“realMealPreference”这样的名字是一个负担,所以Swift允许你重用原来的variables名,在括号范围内创build一个临时variables名

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

下面是一些代码来演示使用不同的variables:

 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")" 

可选的绑定工作通过检查是否可选等于零。 如果不是,则将可选项解包到提供的常量中并执行该块。 在Xcode 8.3和更高版本(Swift 3.1)中,试图打印一个像这样的可选项将导致无用的警告。 使用可选的debugDescription来使其静音:

 print("\(mealPreference.debugDescription)") 

什么是可选项?

可选项有两个用例:

  1. 事情可能会失败(我期待着什么,但我什么都没有得到)
  2. 现在什么都没有,但可能稍后会发生(反之亦然)

一些具体的例子:

  • 一个属性,可以在那里或不在那里,像middleNamespouse在一个Person
  • 一个可以返回值或不返回值的方法,如在数组中search匹配项
  • 一个方法,可以返回一个结果或得到一个错误,不返回任何东西,如试图读取文件的内容(通常返回文件的数据),但文件不存在
  • 委托属性不一定要设置,通常在初始化后设置
  • 对于weak类的属性。 他们指向的东西可以随时设置nil
  • 可能需要释放才能回收内存的大型资源
  • 当你需要一种方法来知道什么时候设置了一个值(数据尚未加载>数据),而不是使用单独的dataLoaded Boolean

Objective-C中不存在可选项,但有一个等价的概念,返回零。 可以返回对象的方法可以返回nil。 这被认为是“没有一个有效的对象”,常常被用来说出事了。 它仅适用于Objective-C对象,不适用于基本types或基本Ctypes(枚举,结构)。 Objective-C通常有专门的types来表示这些值的缺失( NSNotFound实际上是NSIntegerMaxkCLLocationCoordinate2DInvalid代表一个无效的坐标, -1或一些负值也被使用)。 编码员必须知道这些特殊的价值,所以他们必须logging和学习每个案件。 如果一个方法不能把nil作为一个参数,那么必须logging下来。 在Objective-C中, nil是一个指针,就像所有的对象被定义为指针一样,但是nil指向一个特定的(零)地址。 在Swift中, nil是一个文字,意味着没有某种types。


比较nil

你曾经可以使用任何可选的Boolean

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

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

 var ambiguous: Boolean? = false 

它有两种“假”,一种是没有价值的,一种是有价值的,但是价值是false 。 斯威夫特讨厌歧义,所以现在你必须总是检查一个可选的反对nil

你可能想知道可选的Boolean是什么? 与其他可选项一样, .none可能表明价值尚未知晓。 networking通话的另一端可能会有些事情需要花费一些时间进行轮询。 可选的布尔值也被称为“ 三值布尔值 ”


快速的技巧

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

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

这些行都不应该编译。

  • 第一行使用一个string字面值设置一个可选的string,两种不同的types。 即使这是一个Stringtypes是不同的
  • 第二行设置一个可选的string为零,两种不同的types
  • 第三行将可选string与nil,两种不同types进行比较

我将通过一些允许这些线路工作的选项的实现细节。


创build一个可选的

使用? 创build一个可选的语法糖,由Swift编译器启用。 如果你想做的很长,你可以像这样创build一个可选项:

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

这将调用Optional的第一个初始化程序public init(_ some: Wrapped) ,它从括号内使用的types推断可选的关联types。

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

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

设置一个可选项nil

您可以创build一个没有初始值的可选项,或者创build一个初始值为nil (两者都有相同的结果)。

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

允许选项nil由协议ExpressibleByNilLiteral (以前命名为NilLiteralConvertible )启用。 可选是使用Optional的第二个初始化程序public init(nilLiteral: ()) 。 文档说,你不应该使用ExpressibleByNilLiteral除了optionals之外的任何东西,因为这会改变你的代码中的零的含义,但是可以这样做:

 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)") 

相同的协议允许你设置一个已经创build的可选项nil 。 虽然不推荐,但可以直接使用零字面初始值设定项:

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

比较一个可选的nil

选项定义了两个特殊的“==”和“!=”运算符,你可以在Optional定义中看到。 第一个==允许你检查任何可选项是否等于零。 如果关联的types相同,则设置为.none的两个不同的optionals将始终相等。 当你比较零,在幕后Swift创build一个可选的相同的关联types,设置为.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") } 

第二个==运算符允许您比较两个可选项。 两者必须是相同的types,并且该types需要符合Equatable (允许与常规“==”运算符进行比较的协议)。 Swift(推测)解开这两个值,并直接比较它们。 它也处理一个或两个可选项是.none 。 注意与nil文字比较的区别。

此外,它允许您将任何Equatabletypes与可选的包装types进行比较:

 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 { ... } 

命名选项

命名可选types与非可选types不同,没有Swift惯例。 人们避免在名称中添加一些东西来表明它是可选的(如“optionalMiddleName”或“possibleNumberAsString”),并让声明显示它是一个可选types。 当你想命名一些东西来保存可选的值时,这会变得困难。 名字“middleName”意味着它是一个stringtypes,所以当你从中提取string值时,通常可能会以“actualMiddleName”或“unwrappedMiddleName”或“realMiddleName”结尾。 使用可选的绑定,并重新使用variables名解决这个问题。


官方定义

从Swift编程语言中的“基础”

Swift还引入了可选types,这些types处理缺less值的情况。 可选项说:“有价值,等于x”或“根本没有价值”。 可选项类似于在Objective-C中使用零指针,但它们适用于任何types,而不仅仅是类。 Objective-C中的可选项比零指针更安全,更有performance力,也是许多Swift最强大function的核心。

可选项是Swift是一种types安全的语言。 Swift帮助你清楚你的代码可以使用的值的types。 如果你的代码的一部分需要一个string,types安全防止你错误地传递一个Int。 这使您能够在开发过程中尽早捕获并修复错误。


为了完成,这里是1899年的一首关于可选项的诗:

昨天在楼梯上
我遇到了一个不在那里的人
他今天不在了
我希望他能走开
安蒂戈尼什


更多资源:

  • Swift编程指南
  • Swift中的选项(中)
  • WWDC第402节“Swift简介”(14:15左右开始)
  • 更多的可选技巧和窍门

我们来看一个NSError的例子,如果没有返回错误,你可以使它返回Nil。 如果没有错误,给它赋值是毫无意义的。

 var error: NSError? = nil 

这也可以让你有一个默认值。 所以你可以设置一个方法的默认值,如果该函数不传递任何东西

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

你不能在Swift中有一个指向nil的variables – 没有指针,也没有空指针。 但是在一个API中,你通常希望能够指出某种特定的价值,或者缺乏价值 – 例如,我的窗口是否有一个委托,如果是,那么是谁? 可选项是Swift的types安全,内存安全的方式来做到这一点。

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

与Objective-C相反,Swift中没有variables可以包含nil ,所以可选的variablestypes被添加了(variables后缀为“?”):

  var aString = nil //error 

最大的区别是可选variables不直接存储值(作为一个正常的Obj-Cvariables会),它们包含两个状态 :“ 有一个值 ”或“ 有零 ”:

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

那样,你可以在不同情况下检查这些variables:

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

通过使用“!” 后缀,您也可以访问包含在它们中的值, 只要这些值存在 。 (即它不是 ):

 let aString: String? = "Hello, World!" // var anotherString: String = aString //error var anotherString: String = aString! println(anotherString) //it will print "Hello, World!" 

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

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

在目标C中,没有值的variables等于'nil'(也可以使用与'0'相同的'nil'值和false),因此可以在条件语句中使用variables(variables的值与'TRUE '和没有值的那些等于'FALSE')。

Swift通过提供“可选值”来提供types安全性。 即防止分配不同types的variables而形成的错误。

所以在Swift中,只有条件语句可以提供布尔值。

 var hw = "Hello World" 

在这里,虽然'hw'是一个string,但是它不能用在像if中的if语句中。

 //This is an error if hw {..} 

为此,需要将其创build为,

 var nhw : String? = "Hello World" //This is correct if nhw {..} 

可选值可以让你显示没有价值。 有点像SQL中的NULL或Objective-C中的NSNull。 我想这将是一个改进,你甚至可以使用这个“原始”types。

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

摘录自:苹果公司“Swift编程语言”,iBooks。 https://itun.es/gb/jEUH0.l

一个可选的手段,斯威夫特不完全确定,如果值对应的types:例如,诠释? 意味着Swift并不完全确定这个数字是否是Int。

要删除它,有三种方法可以使用。

1)如果您完全确定types,可以使用感叹号强制拆包,如下所示:

 // Here is an optional variable: var age: Int? // Here is how you would force unwrap it: var unwrappedAge = age! 

如果你强制打开一个可选的等于零,你可能会遇到这个崩溃错误:

在这里输入图像说明

这不一定安全,所以这里有一个方法可以防止在你不确定types和值的情况下崩溃:

方法2和方法3防止这个问题。

2)隐式解包可选

  if let unwrappedAge = age { // continue in here } 

请注意,解包types现在是Int ,而不是Int?

3)警戒声明

  guard let unwrappedAge = age else { // continue in here } 

从这里,你可以继续使用解包variables。 如果您确定variables的types,请确保只强制展开(使用!)。

祝你的项目好运!

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

参考下面的资源深入了解.. https://developer.apple.com/library/prerelease/mac/documentation/Swift/Conceptual/Swift_Programming_Language/OptionalChaining.html

让我们试试下面的代码游乐场 。我希望能清楚的知道什么是可选的,以及使用它的原因。

 var sampleString: String? ///Optional, Possible to be nil sampleString = nil ////perfactly valid as its optional sampleString = "some value" //Will hold the value if let value = sampleString{ /// the sampleString is placed into value with auto force upwraped. print(value+value) ////Sample String merged into Two } sampleString = nil // value is nil and the if let value = sampleString{ print(value + value) ///Will Not execute and safe for nil checking } // print(sampleString! + sampleString!) //this line Will crash as + operator can not add nil 

当我开始学习Swift时,很难理解为什么可选

让我们这样想。 考虑一个类有两个属性namecompany

 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 } } 

现在让我们创buildPerson几个对象

 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? 。 让我们考虑一下,如apple将是apple Inc公司名称后,我们要添加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. 

现在让我们来研究一下为什么可选scheme到位

 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!!! } 

tom更换bob

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

祝贺! 我们已经妥善处理optional?

所以实现点是

  1. 如果可能nil我们将把variables标记为可选项
  2. 如果我们想在代码中的某个地方使用这个variables,编译器会提醒你我们需要检查一下,如果它包含nil ,我们是否有适当的处理这个variables。

谢谢你…快乐编码

好…

(可选)表示您的variables可能包含一个零值 (unwrapper)表示你的variables在运行时必须有一个内存(或者值)(当试图从中获取一个值)。

主要的区别在于可选的链接在可选项为nil时会失败,而当可选为nil时,强制解包会触发运行时错误。

为了体现这样一个事实,即可以在一个nil值上调用可选的链接,即使正在查询的属性,方法或脚标返回非可选值,可选的链接调用的结果也总是可选值。 您可以使用此可选返回值来检查可选的链接调用是否成功(返回的可选值包含一个值),或者由于链中的nil值(返回的可选值为nil)而不成功。

具体来说,可选的链接调用的结果与预期的返回值是相同的types,但包含在可选的。 通常返回一个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 

这是苹果开发委员会详细的基本教程。

这是Swift中的一个等价的可选声明:

 var middleName: String? 

这个声明创build一个名为middleNametypes为String的variables。 Stringvariablestypes​​后面的问号(?)表示middleNamevariables可以包含一个可以是String或者nil的值。 任何查看此代码的人都会立即知道middleName可能为零。 这是自我logging!

如果您没有为可选常量或variables指定初始值(如上所示),则该值将自动设置为零。 如果你愿意,你可以明确地设置初始值为零:

 var middleName: String? = nil 

欲了解更多详细信息可选阅读以下链接

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