来自Apple的文档:
您可以使用
if
和let
一起处理可能缺少的值.这些值表示为选项.可选值包含值或包含nil
以指示缺少值.?
在值的类型后面写一个问号()以将值标记为可选.
为什么要使用可选值?
nev*_*ing 509
Swift中的可选项是一种可以保存值或不保存值的类型.通过将a附加?
到任何类型来编写选项:
var name: String? = "Bertie"
Run Code Online (Sandbox Code Playgroud)
Optionals(以及Generics)是最难理解的Swift概念之一.由于它们的编写和使用方式,很容易弄清楚它们是什么.将上面的可选项与创建普通String进行比较:
var name: String = "Bertie" // No "?" after String
Run Code Online (Sandbox Code Playgroud)
从语法看起来,可选的String与普通的String非常相似.不是.可选的String不是打开某些"可选"设置的String.它不是一种特殊的String.String和可选String是完全不同的类型.
这是最重要的事情:可选是一种容器.可选的String是一个可能包含String的容器.可选的Int是一个可能包含Int的容器.将可选项视为一种包裹.在打开它之前(或用选项语言"展开"),你不会知道它是否包含任何东西.
通过在任何Swift文件中键入"Optional"并⌘-单击它,您可以在Swift标准库中查看选项的实现方式.这是定义的重要部分:
enum Optional<Wrapped> {
case none
case some(Wrapped)
}
Run Code Online (Sandbox Code Playgroud)
可选是一个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
}
Run Code Online (Sandbox Code Playgroud)
您可以比较可选项以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")
}
Run Code Online (Sandbox Code Playgroud)
这有点令人困惑.这意味着可选的是一件事或另一件事.它要么是零,要么是"鲍勃".事实并非如此,可选项不会转换为其他内容.将它与nil进行比较是制作易于阅读的代码的一种技巧.如果可选等于nil,这只意味着枚举当前设置为.none
.
如果您尝试将非可选变量设置为nil,则会出现错误.
var red: String = "Red"
red = nil // error: nil cannot be assigned to type 'String'
Run Code Online (Sandbox Code Playgroud)
查看选项的另一种方法是作为普通Swift变量的补充.它们是保证具有值的变量的对应物.斯威夫特是一种讨厌模糊的语言.大多数变量被定义为非选项,但有时这是不可能的.例如,假设一个视图控制器从高速缓存或网络加载图像.在创建视图控制器时,它可能有也可能没有该图像.无法保证图像变量的值.在这种情况下,您必须使其成为可选项.它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.
Run Code Online (Sandbox Code Playgroud)
因为在打开和使用可选项之前应始终检查nil,这是一种常见模式:
var mealPreference: String? = "Vegetarian"
if mealPreference != nil {
let unwrappedMealPreference: String = mealPreference!
print("Meal: \(unwrappedMealPreference)") // or do something useful
}
Run Code Online (Sandbox Code Playgroud)
在这种模式中,您检查是否存在值,然后当您确定它是时,您强制将其打包到临时常量中以供使用.因为这是一件很常见的事情,Swift提供了一个使用"if let"的快捷方式.这称为"可选绑定".
var mealPreference: String? = "Vegetarian"
if let unwrappedMealPreference: String = mealPreference {
print("Meal: \(unwrappedMealPreference)")
}
Run Code Online (Sandbox Code Playgroud)
这会创建一个临时常量(或替换let
为变量var
),其范围仅在if的大括号内.因为必须使用像"unwrappedMealPreference"或"realMealPreference"这样的名称是一种负担,Swift允许您重用原始变量名称,在括号范围内创建一个临时变量名称
var mealPreference: String? = "Vegetarian"
if let mealPreference: String = mealPreference {
print("Meal: \(mealPreference)") // separate from the other mealPreference
}
Run Code Online (Sandbox Code Playgroud)
这里有一些代码来演示使用不同的变量:
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")"
Run Code Online (Sandbox Code Playgroud)
可选绑定通过检查可选等于nil来工作.如果没有,则将可选内容解包为提供的常量并执行该块.在Xcode 8.3及更高版本(Swift 3.1)中,尝试打印这样的可选项会导致无用的警告.使用可选项将debugDescription
其静音:
print("\(mealPreference.debugDescription)")
Run Code Online (Sandbox Code Playgroud)
Optionals有两个用例:
一些具体的例子:
middleName
或spouse
在Person
类中weak
类中的属性.他们指出,可以设置为东西nil
在任何时间Boolean
Objective-C中不存在选项,但是有一个等价的概念,返回nil.可以返回对象的方法可以返回nil.这被认为是指"没有有效物体",并且常常被用来说出错了.它仅适用于Objective-C对象,而不适用于基元或基本C类型(枚举,结构).Objective-C通常有专门的类型来表示缺少这些值(NSNotFound
实际上NSIntegerMax
,kCLLocationCoordinate2DInvalid
表示无效坐标,-1
或者也使用某些负值).编码人员必须了解这些特殊值,因此必须记录并学习每种情况.如果方法不能nil
作为参数,则必须记录.在Objective-C中,nil
指针就像所有对象都被定义为指针一样,但是nil
指向一个特定的(零)地址.在Swift中,nil
是一个文字,意味着缺少某种类型.
nil
您曾经能够使用任何可选的Boolean
:
let leatherTrim: CarExtras? = nil
if leatherTrim {
price = price + 1000
}
Run Code Online (Sandbox Code Playgroud)
在更新版本的Swift中你必须使用leatherTrim != nil
.为什么是这样?问题是a Boolean
可以包装在一个可选项中.如果你有Boolean
这样的:
var ambiguous: Boolean? = false
Run Code Online (Sandbox Code Playgroud)
它有两种"假",一种没有价值,一种有价值,但价值却是false
.斯威夫特讨厌模棱两可,所以现在你必须经常检查一个可选项nil
.
您可能想知道可选项的重点Boolean
是什么?与其他选项一样,.none
州可以表明该值尚未知.网络呼叫的另一端可能有某些东西需要一些时间来轮询.可选的布尔也称为" 三值布尔 "
Swift使用一些技巧来允许选项工作.考虑这三行普通可选代码;
var religiousAffiliation: String? = "Rastafarian"
religiousAffiliation = nil
if religiousAffiliation != nil { ... }
Run Code Online (Sandbox Code Playgroud)
这些行都不应该编译.
String
类型是不同的我将介绍允许这些行工作的选项的一些实现细节.
使用?
创建可选的语法糖,由斯威夫特编译器启用.如果你想做很长的事,你可以创建一个像这样的可选项:
var name: Optional<String> = Optional("Bob")
Run Code Online (Sandbox Code Playgroud)
这调用了Optional
第一个初始化程序,public init(_ some: Wrapped)
它从括号中使用的类型推断出可选的关联类型.
创建和设置可选项的更长方法:
var serialNumber:String? = Optional.none
serialNumber = Optional.some("1234")
print("\(serialNumber.debugDescription)")
Run Code Online (Sandbox Code Playgroud)
nil
您可以创建一个没有初始值的可选项,也可以创建一个初始值为nil
(两者都具有相同结果)的可选项.
var name: String?
var name: String? = nil
Run Code Online (Sandbox Code Playgroud)
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)")
Run Code Online (Sandbox Code Playgroud)
相同的协议允许您将已创建的可选项设置为nil
.虽然不推荐,但您可以直接使用nil literal初始化程序:
var name: Optional<String> = Optional(nilLiteral: ())
Run Code Online (Sandbox Code Playgroud)
nil
Optionals定义了两个特殊的"=="和"!="运算符,您可以在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")
}
Run Code Online (Sandbox Code Playgroud)
第二个==
运算符允许您比较两个选项.两者都必须是相同的类型,并且该类型需要符合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!"
}
Run Code Online (Sandbox Code Playgroud)
在幕后,Swift在比较之前将非可选项作为可选项包装.它也适用于文字(if 23 == numberFromString {
)
我说有两个==
操作符,但实际上有三个操作符允许你放在nil
比较的左侧
if nil == name { ... }
Run Code Online (Sandbox Code Playgroud)
命名可选类型与非可选类型不同,没有Swift约定.人们避免在名称中添加内容以显示它是可选的(如"optionalMiddleName"或"possibleNumberAsString"),并让声明显示它是可选类型.当你想要命名某些东西以保存可选项中的值时,这会变得很困难.名称"middleName"意味着它是一个String类型,因此当您从中提取String值时,通常最终会得到"actualMiddleName"或"unwrappedMiddleName"或"realMiddleName"等名称.使用可选绑定并重用变量名来解决此问题.
Swift还引入了可选类型,它们处理缺少值.Optionals说"有一个值,它等于x"或"根本没有值".Optionals类似于在Objective-C中使用带有指针的nil,但它们适用于任何类型,而不仅仅是类.Option-C中的可选项比nil指针更安全,更具表现力,是Swift最强大功能的核心.
可选项是Swift是一种类型安全语言这一事实的一个例子.Swift可以帮助您清楚代码可以使用的值的类型.如果您的部分代码需要String,则类型安全性会阻止您错误地将其传递给Int.这使您能够在开发过程中尽早捕获并修复错误.
最后,这是1899年关于选项的一首诗:
昨天在楼梯上
我遇到了一个不在那里的男人
他今天不再在那里
我希望,我希望他能离开
Antigonish
Joh*_*ato 16
让我们以a为例NSError
,如果没有返回错误,你可以选择让它返回Nil.如果没有错误,为它赋值是没有意义的.
var error: NSError? = nil
Run Code Online (Sandbox Code Playgroud)
这也允许您拥有默认值.因此,如果函数未传递任何内容,则可以将方法设置为默认值
func doesntEnterNumber(x: Int? = 5) -> Bool {
if (x == 5){
return true
} else {
return false
}
}
Run Code Online (Sandbox Code Playgroud)
ric*_*ter 12
你不能拥有一个指向nil
Swift 的变量- 没有指针,也没有空指针.但是在API中,您通常希望能够指示特定类型的值或缺少值 - 例如,我的窗口是否有委托,如果是,那么它是谁?可选项是Swift的类型安全,内存安全的方法.
Teo*_*aru 11
我做了一个简短的回答,总结了上面的大部分内容,以清除我作为初学者的不确定性:
与Objective-C相反,Swift中没有变量可以包含nil,因此添加了Optional变量类型(变量后缀为"?"):
var aString = nil //error
Run Code Online (Sandbox Code Playgroud)
最大的区别是Optional变量不直接存储值(正常的Obj-C变量)它们包含两个状态:" 有一个值 "或" 有nil ":
var aString: String? = "Hello, World!"
aString = nil //correct, now it contains the state "has nil"
Run Code Online (Sandbox Code Playgroud)
也就是说,您可以在不同情况下检查这些变量:
if let myString = aString? {
println(myString)
}
else {
println("It's nil") // this will print in our case
}
Run Code Online (Sandbox Code Playgroud)
通过使用"!" 后缀,您也可以访问包含在其中的值,只有存在这些值.(即它不是零):
let aString: String? = "Hello, World!"
// var anotherString: String = aString //error
var anotherString: String = aString!
println(anotherString) //it will print "Hello, World!"
Run Code Online (Sandbox Code Playgroud)
这就是你需要使用"?"的原因.和"!" 并且默认情况下不使用所有这些.(这是我最大的困惑)
我也同意上面的答案:可选类型不能用作布尔值.
在目标C中,没有值的变量等于'nil'(也可以使用与n和false相同的'nil'值),因此可以在条件语句中使用变量(具有值的变量与'TRUE'相同'和那些没有价值观的人等于'假').
Swift通过提供"可选值"来提供类型安全性.即它可以防止因分配不同类型的变量而形成的错误.
所以在Swift中,只能在条件语句中提供布尔值.
var hw = "Hello World"
Run Code Online (Sandbox Code Playgroud)
在这里,偶数'虽然'hw'是一个字符串,但它不能在if语句中使用,就像在Objective C中一样.
//This is an error
if hw
{..}
Run Code Online (Sandbox Code Playgroud)
为此,它需要创建为,
var nhw : String? = "Hello World"
//This is correct
if nhw
{..}
Run Code Online (Sandbox Code Playgroud)
可选值允许您显示缺少值.有点像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)”
Run Code Online (Sandbox Code Playgroud)
摘录自:Apple Inc."The Swift Programming Language."iBooks.https://itun.es/gb/jEUH0.l