什么是Swift中的可选值?
从苹果的文档 :
您可以使用
if
和let
来处理可能丢失的值。 这些值表示为可选项。 一个可选值包含一个值或者包含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一个临时常量(或者,如果您将let
replace为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)")
什么是可选项?
可选项有两个用例:
- 事情可能会失败(我期待着什么,但我什么都没有得到)
- 现在什么都没有,但可能稍后会发生(反之亦然)
一些具体的例子:
- 一个属性,可以在那里或不在那里,像
middleName
或spouse
在一个Person
- 一个可以返回值或不返回值的方法,如在数组中search匹配项
- 一个方法,可以返回一个结果或得到一个错误,不返回任何东西,如试图读取文件的内容(通常返回文件的数据),但文件不存在
- 委托属性不一定要设置,通常在初始化后设置
- 对于
weak
类的属性。 他们指向的东西可以随时设置nil
- 可能需要释放才能回收内存的大型资源
- 当你需要一种方法来知道什么时候设置了一个值(数据尚未加载>数据),而不是使用单独的dataLoaded
Boolean
Objective-C中不存在可选项,但有一个等价的概念,返回零。 可以返回对象的方法可以返回nil。 这被认为是“没有一个有效的对象”,常常被用来说出事了。 它仅适用于Objective-C对象,不适用于基本types或基本Ctypes(枚举,结构)。 Objective-C通常有专门的types来表示这些值的缺失( NSNotFound
实际上是NSIntegerMax
, kCLLocationCoordinate2DInvalid
代表一个无效的坐标, -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。 即使这是一个
String
types是不同的 - 第二行设置一个可选的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
文字比较的区别。
此外,它允许您将任何Equatable
types与可选的包装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。 多个查询可以链接在一起,如果链中的任何链接都是零,则整个链将优雅地失败。
让我们试试下面的代码游乐场 。我希望能清楚的知道什么是可选的,以及使用它的原因。
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
时,很难理解为什么可选 。
让我们这样想。 考虑一个类有两个属性name
和company
。
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?
所以实现点是
- 如果可能
nil
我们将把variables标记为可选项 - 如果我们想在代码中的某个地方使用这个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