可以同时使用
if
和let
来处理可能缺少的值.这些值表示为可选值.可选值包含一个值或包含nil
以指示缺少该值.在值的类型后写一个问号(?
),将该值标记为可选值.
为什么要使用可选值?
可以同时使用
if
和let
来处理可能缺少的值.这些值表示为可选值.可选值包含一个值或包含nil
以指示缺少该值.在值的类型后写一个问号(?
),将该值标记为可选值.
为什么要使用可选值?
Swift中的可选类型是一种既可以保存值也可以不保存值的类型.可选项是通过在任何类型上添加?
来编写的:
var name: String? = "Bertie"
Optionals(以及泛型)是最难理解的Swift概念之一.由于它们的书写和使用方式,人们很容易对它们产生错误的认识.将上述选项与创建普通字符串进行比较:
var name: String = "Bertie" // No "?" after String
从语法上看,可选字符串与普通字符串非常相似.不是.可选字符串不是启用了某些"可选"设置的字符串.这不是一种特殊的弦.字符串和可选字符串是完全不同的类型.
这里有一件最重要的事情要知道:可选的是一种容器.可选字符串是可能包含字符串的容器.可选整数是可能包含整数的容器.将可选整数视为一种Package.在你打开它(或者用optionals的语言"展开")之前,你不知道它包含什么或什么.
通过在任何Swift文件中键入"可选"并⌘-点击它.以下是定义的重要部分:
enum Optional<Wrapped> {
case none
case some(Wrapped)
}
可选的是enum
,它可以是两种情况之一:.none
或.some
.如果是.some
,则有一个关联值,在上面的示例中,它将是String
"Hello".可选的使用泛型为关联的值指定类型.可选字符串的类型不是String
,而是Optional
,或者更准确地说是Optional<String>
.
Swift 对optionals所做的一切都是魔法,可以让读写代码更加流畅.不幸的是,这掩盖了它的实际工作方式.稍后我会介绍一些技巧.
Note:我会经常谈论可选变量,但是创建可选常量也可以.我用它们的类型标记所有变量,以便更容易理解正在创建的类型,但您不必在自己的代码中这样做.
如果要创建可选的类型,请在?
之后进行换行.任何类型都可以是可选的,甚至是您自己的自定义类型.类型和?
之间不能有空格.
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,则会出现错误.
var red: String = "Red"
red = nil // error: nil cannot be assigned to type 'String'
另一种看待期权的方式是作为普通Swift变量的补充.它们是保证有值的变量的对应项.Swift 是一种谨慎的语言,不喜欢模棱两可.大多数变量被定义为非可选变量,但有时这是不可能的.例如,想象一个视图控制器从缓存或网络加载图像.在创建视图控制器时,它可能有也可能没有该图像.无法保证image变量的值.在这种情况下,您必须将其设置为可选.它从nil
开始,当检索图像时,可选的获取一个值.
使用可选选项可以揭示程序员的意图.与Objective-C相比,在Objective-C中,任何对象都可能为零,Swift需要您明确某个值何时会丢失,以及该值何时保证存在.
可选的String
不能代替实际的String
.要在可选文件中使用包装的值,必须将其展开.打开可选名称的最简单方法是在可选名称后添加!
.这被称为"强制展开".它返回optional中的值(作为原始类型),但如果optional为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)中,try 打印这样的可选文件将导致无用的警告.使用可选的debugDescription
将其静音:
print("\(mealPreference.debugDescription)")
Optionals有两个用例:
一些具体例子:
Person
类中的middleName
或spouse
weak
个属性.他们指向的东西可以随时设置为nil
Boolean
Optionals在Objective-C中不存在,但有一个等价的概念,返回nil.可以返回对象的方法可以返回nil.这被理解为"缺少有效对象"的意思,通常用来表示出现了问题.它只适用于Objective-C对象,不适用于原语或基本C类型(枚举、 struct ).Objective-C通常有专门的类型来表示这些值的缺失(NSNotFound
表示NSIntegerMax
,kCLLocationCoordinate2DInvalid
表示无效坐标,-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
的类型是不同的我将介绍允许这些行工作的选项的一些实现细节.
使用?
创建一个可选的是语法糖,由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
允许optionals等于nil
是由协议ExpressibleByNilLiteral
(以前称为NilLiteralConvertible
)启用的.可选项是用Optional
的第二个初始值设定项public init(nilLiteral: ())
创建的.文档中说,除了optionals之外,你不应该使用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 literal初始值设定项:
var name: Optional<String> = Optional(nilLiteral: ())
nil
选项定义了两个特殊的"=="和"!="运算符,可以在Optional
定义中看到.第一个==
允许您判断任何可选值是否等于零.设置为的两个不同选项.如果关联的类型相同,则所有类型都不相等.当创建一个相同类型的Swift时,可 Select 将其与"零"进行比较.没有人会用这个来做比较.
// 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"或"根本没有值".Optionals类似于在Objective-C中对指针使用nil,但它们适用于任何类型,而不仅仅是类.Optionals比Objective-C中的零指针更安全、更具表现力,是Swift许多最强大功能的核心.
Optionals就是一个例子,表明Swift是一种类型安全的语言.Swift帮助您明确代码可以使用的值的类型.如果您希望Int类型的安全代码一部分一部分地传递,则可以防止它.这使您能够在开发过程中尽早捕获并修复错误.
最后,这里有一首1899年的关于optionals的诗:
Yesterday upon the stair
I met a man who wasn’t there
He wasn’t there again today
I wish, I wish he’d go away
Antigonish