在Swift中什么是可选值?

苹果公司的文档:

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

为什么要使用可选值?

158817 次浏览

让我们以NSError为例,如果没有返回错误,你想让它可选地返回Nil。如果没有错误,给它赋值是没有意义的。

var error: NSError? = nil

这也允许您拥有一个默认值。因此,如果函数没有传递任何东西,你可以为方法设置一个默认值

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

在Swift中,你不能有一个指向nil的变量——没有指针,也没有空指针。但是在一个API中,你经常希望能够指出一个特定类型的值,或者一个缺乏值的值——例如,我的窗口是否有一个委托,如果有,它是谁?可选的是Swift的类型安全,内存安全的方式来做到这一点。

可选值允许您显示没有值。有点像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)”

摘自:苹果公司《快速编程语言》。“iBooks。https://itun.es/gb/jEUH0.l

在Swift中,可选类型既可以有值,也可以没有值。可选对象是通过将?附加到任何类型来编写的:

var name: String? = "Bertie"

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

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

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

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

你可以在Swift标准库中键入"Optional"进入任何Swift文件,并⌘-点击它。下面是定义的重要部分:

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

Optional只是一个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
}

使用可选的

你可以将一个可选对象与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'

另一种看待可选项的方式是作为正常Swift变量的补充。它们与保证有值的变量相对应。Swift是一种谨慎的语言,讨厌歧义。大多数变量都被定义为非可选变量,但有时这是不可能的。例如,想象一个视图控制器从缓存或网络加载图像。在视图控制器创建时,它可能有也可能没有那个图像。没有办法保证图像变量的值。在这种情况下,你必须让它成为可选的。它以nil开始,当检索图像时,可选选项获得一个值。

使用可选对象可以揭示程序员的意图。与Objective-C中任何对象都可以为nil相比,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.

检查和使用一个可选的

因为你应该总是检查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)")

什么是可选的?

可选项有两个用例:

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

一些具体的例子:

  • 一个可以存在也可以不存在的属性,如Person类中的middleNamespouse
  • 一个可以返回值或不返回的方法,例如在数组中搜索匹配项
  • 一个方法,它可以返回一个结果,也可以得到一个错误,但不返回任何结果,就像试图读取文件的内容(通常返回文件的数据),但文件不存在
  • 委托属性,这些属性并不总是必须设置,通常在初始化后设置
  • 类中的weak属性。它们所指向的对象可以在任何时候被设置为nil
  • 为了回收内存,可能必须释放一个大型资源
  • 当你需要一种方法来知道一个值何时被设置(数据尚未加载>the data)而不是使用单独的dataLoaded Boolean

Objective-C中不存在可选项,但有一个等效的概念,返回nil。可以返回对象的方法可以返回nil。这被理解为“有效对象的缺失”。经常用来表示某事出了问题。它只适用于Objective-C对象,而不适用于原语或基本的c类型(枚举、结构体)。Objective-C通常有专门的类型来表示这些值的缺失(NSNotFound实际上是NSIntegerMaxkCLLocationCoordinate2DInvalid表示一个无效的坐标,-1或一些负值也会被使用)。编码器必须了解这些特殊值,因此必须为每种情况记录和学习它们。如果一个方法不能接受nil作为参数,这必须被记录下来。在Objective-C中,nil是一个指针,就像所有对象都被定义为指针一样,但是nil指向一个特定的(零)地址。在Swift中,nil是一个字面量,这意味着缺少某种类型。


nil相比

你过去可以使用任意可选类型作为Boolean:

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

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

var ambiguous: Boolean? = false

它有两种“false”,一种是没有值,另一种是有值但值为false。Swift讨厌模棱两可,所以现在你必须总是检查nil的可选。

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


斯威夫特的技巧

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

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

这些行都不能编译。

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

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


创建一个可选

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

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

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

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

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

将一个可选参数设置为nil

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

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

允许可选选项等于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)")

同样的协议允许你将一个已经创建的可选项设置为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 { ... }

命名可选

Swift没有一个约定来区分可选类型和非可选类型。人们避免在名称中添加一些东西来表明它是可选的(如“optionalmiddleame"或“possibleNumberAsString"),并让声明表明它是可选类型。当您想要命名一些东西来保存可选对象的值时,这就变得困难了。名字“middle lename "意味着它是一个String类型,所以当你从中提取String值时,你通常可以以“;actualmiddlename &;或“;unwrappedMiddleName"或“;realMiddleName"。使用可选绑定并重用变量名来解决这个问题。


官方定义

“Basics"Swift编程语言:

Swift还引入了可选类型,用于处理没有值的情况。可选选项说“有一个值,它等于x”或“根本没有值”。可选对象类似于Objective-C中对指针使用nil,但它们适用于任何类型,而不仅仅是类。在Objective-C中,可选对象比nil指针更安全,表达能力更强,是Swift许多最强大特性的核心。

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


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

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

< em > Antigonish < / em > < / p >


更多资源:

在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


{..}

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

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

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

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

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

    var aString = nil //error

最大的区别是可选变量不直接存储值(像普通的Obj-C变量那样),它们包含两种状态: "有一个值"或"已经为零":

    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
}

通过使用"!"后缀,你也可以访问它们包装的值只有当它们存在时。(即它不是):

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


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

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

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

下面是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

可选意味着Swift不能完全确定该值是否与类型对应:例如,Int?这意味着Swift不能完全确定该数字是否是Int类型。

要去除它,可以采用三种方法。

1)如果你绝对确定它的类型,你可以使用感叹号来强制打开它,就像这样:

// Here is an optional variable:


var age: Int?


// Here is how you would force unwrap it:


var unwrappedAge = age!

如果你强制打开一个可选对象并且它等于nil,你可能会遇到这个崩溃错误:

enter image description here

这并不一定是安全的,所以这里有一个方法,可以防止崩溃,如果你不确定类型和值:

方法二和方法三防止了这个问题。

2)隐式解包装可选

 if let unwrappedAge = age {


// continue in here


}

注意,解包装类型现在是Int,而不是Int ?

3)警卫声明

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

在这里,您可以继续使用未包装变量。如果您确定变量的类型,请确保仅强制展开(使用!)。

祝你的项目好运!

让我们用下面的代码操场上进行实验。我希望能明确什么是可选的和使用它的原因。

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时,很难意识到为什么是可选的

让我们这样想。 让我们考虑一个类Person,它有两个属性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
        

}
}

现在让我们创建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!!!
}

让我们用tom替换bob

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

祝贺!我们已经正确地处理了optional?

实现要点是

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

谢谢你!快乐的编码

嗯…

? (可选)表示你的变量可能包含一个nil值,而! (打开)表示你的变量在运行时被使用(试图从它得到一个值)时必须有一个内存(或值)。

主要的区别是,当可选对象为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

下面是苹果开发者委员会编写的基本教程:可选的链接

在Swift中,可选类型既可以有值,也可以没有值。可选对象是通过将?附加到任何类型来编写的:

var name: String?

你可以参考这个链接来深入了解:https://medium.com/@agoiabeladeyemi/optionals-in-swift-2b141f12f870

有很多错误是由人们试图使用一个没有设置的值引起的,有时这可能会导致崩溃,在objective - c中,试图调用nil对象引用的方法会被忽略,所以你的一些代码没有执行,编译器或编写的代码无法告诉你为什么。一个可选的参数让你有永远不能为nil的变量,如果你试图构建它,编译器会在你的代码甚至有机会运行之前告诉你,或者你可以决定它适合于对象为undefined,然后编译器会告诉你,当你试图写一些没有考虑到这一点的东西。

在调用nil对象的情况下,你可以直接

object?.doSomthing()

你已经明确地告诉编译器和任何阅读你代码的人,它的可能对象是nil,什么都不会发生。有时,只有在值存在时才希望出现几行代码,因此可以这样做

if let obj = object {
obj.doSomthing()
doSomethingto(obj)
}

这两个语句只会在object是什么东西时执行,简单地说,如果它不是什么东西,你可能想要停止整个代码块的其余部分

guard let obj = object {
return
}
obj.doSomthing()
doSomethingto(obj)

如果后面的所有内容都只适用于object,那么阅读起来会更简单,另一种可能是你想使用默认值

let obj = object ?? <default-object>
obj.doSomthing()
doSomethingto(obj)

现在obj将被赋值给某个对象,即使它是该类型的默认值

选项是有用的情况下一个值可能不会获得一个值,直到一些事件发生或您可以使用一个选项设置为零,以说它不再相关的或者需要设置和使用的一切它没有做任何与它,直到它是集,我喜欢用可选的方法之一是告诉我必须做点什么或者已经完成

func eventFired() {
guard timer == nil else { return }
timer = scheduleTimerToCall(method, in: 60)
}


func method() {
doSomthing()
timer = nil
}

这个sudo代码可以多次调用eventFired,但只有在第一次调用时才会调度一个计时器,一旦调度执行,它会运行一些方法并将计时器设置为nil,这样就可以调度另一个计时器。

一旦你明白了变量处于未定义状态你就可以用它来做任何事情。

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

var lol: Laugh? = nil

等价于Kotlin:

var lol: Laugh? = null

在Java中是这样的:

@Nullable Laugh lol = null;

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