gpt4 book ai didi

swift - Swift中的 optional 值是什么?

转载 作者:行者123 更新时间:2023-11-30 12:25:41 26 4
gpt4 key购买 nike

Apple's documentation


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


为什么要使用可选值?

最佳答案

Swift中的可选类型是可以保存值或不保存值的类型。通过将?附加到任何类型来编写可选内容:

var name: String? = "Bertie"


可选(以及泛型)是最难理解的Swift概念之一。由于它们是如何编写和使用的,很容易对它们是什么有一个错误的认识。比较上面的可选内容与创建普通的String:

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


从语法上看,可选字符串与普通字符串非常相似。不是。可选字符串不是打开了某些“可选”设置的字符串。它不是String的特殊种类。字符串和可选字符串是完全不同的类型。

这是要了解的最重要的事情:可选容器是一种容器。可选的String是一个可能包含String的容器。可选的Int是一个可能包含Int的容器。将可选件视为一种包裹。在您打开它(或用可选语言“解开”)之前,您将不会知道它是否包含某些东西或什么都不包含。

您可以在Swift标准库中看到 how optionals are implemented,方法是在任何Swift文件中键入“可选”并⌘单击它。这是定义的重要部分:

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


可选的只是一个 enum,它可以是以下两种情况之一: .none.some。如果是 .some,则有一个关联的值,在上面的示例中,该值将是 String“ Hello”。可选的使用泛型为关联值赋予类型。可选字符串的类型不是 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进行比较是使代码易于阅读的一个技巧。如果可选值等于nil,则仅表示该枚举当前设置为 .none



只有可选参数可以为nil

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

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


查看可选变量的另一种方法是对普通Swift变量的补充。它们与保证具有值的变量相对应。斯威夫特(Swift)是一种小心翼翼的语言,讨厌歧义。大多数变量被定义为非可选变量,但是有时这是不可能的。例如,假设一个视图控制器从缓存或从网络加载图像。创建视图控制器时,它可能有也可能没有该图像。无法保证image变量的值。在这种情况下,您必须将其设为可选。它以 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.




检查并使用可选

因为在展开和使用可选选项之前应始终检查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)")
}


这将创建一个临时常量(如果将 let替换为 var,则为变量),其范围仅在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)")




选件有什么用?

可选项目有两个用例:


可能失败的事情(本来是我期待的东西,但我一无所获)
现在什么都不是,但以后可能又会发生(反之亦然)


一些具体的例子:


一个可以存在或不存在的属性,例如 middleName类中的 spousePerson
一种可以返回值或不返回任何值的方法,例如在数组中搜索匹配项
一种可以返回结果或获取错误但不返回任何内容的方法,例如尝试读取文件的内容(通常会返回文件的数据)但该文件不存在
委托属性,通常不必在初始化后进行设置
对于类中的 weak属性。他们指向的东西可以随时设置为 nil
可能必须释放大量资源才能回收内存
当您需要一种方法来知道何时设置值(尚未加载的数据>数据)而不是使用单独的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。 Swift讨厌歧义,因此现在您必须始终针对 nil检查可选项。

您可能想知道可选的 Boolean的意义是什么?与其他可选内容一样, .none状态可能表明该值尚未确定。网络通话的另一端可能有一些内容需要花费一些时间进行轮询。可选的布尔值也称为“ Three-Value Booleans



迅速的把戏

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

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


这些行都不应该编译。


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


我将详细介绍一些可选的实现细节,以使这些行起作用。



创建一个可选的

使用 ?创建可选的是语法糖,由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约定来命名可选类型和非可选类型。人们避免在名称中添加任何东西来表明它是可选的(例如“ optionalMiddleName”或“ possibleNumberAsString”),而让声明表明它是可选的类型。当您想命名某个名称以保留可选值时,这将变得很困难。名称“ middleName”表示它是一种字符串类型,因此从中提取字符串值时,通常经常会以“ actualMiddleName”或“ unwrappedMiddleName”或“ realMiddleName”之类的名称结尾。使用可选的绑定并重用变量名来解决此问题。



官方定义

"The Basics" in the Swift Programming Language


  Swift还引入了可选类型,用于处理缺少值的情况。可选的选项是“有一个值,它等于x”或“根本没有值”。可选参数类似于在Objective-C中对指针使用nil,但是它们适用于任何类型,而不仅仅是类。与Objective-C中的零指针相比,可选选项更安全,更具表达力,并且是Swift最强大功能的核心。
  
  可选是Swift是一种类型安全语言的示例。 Swift可以帮助您弄清楚代码可以使用的值的类型。如果代码的一部分需要一个字符串,则类型安全性可防止您误将其传递给Int。这使您能够在开发过程中尽早发现并修复错误。




最后,这是1899年关于可选内容的诗:

昨天在楼梯上
我遇到一个不在那里的人
他今天不在
我希望,我希望他能走
Antigonish



更多资源:


The Swift Programming Guide
Optionals in Swift (Medium)
WWDC Session 402 "Introduction to Swift" (starts around 14:15)
More optional tips and tricks

关于swift - Swift中的 optional 值是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44250725/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com