Swift6.0基础知识 -- 可选2
目录
- 1、nil
- 2、可选绑定
- 3、提供后备值
- 4、强制解包
- 4、隐式解包可选
在可能缺失值的情况下,请使用 可选。可选代表两种可能性:要么 存在一个指定类型的值,并可以解包可选以访问该值;要么 根本就没有值。
举一个可能缺失值的例子,Swift 的 Int
类型有一个初始化器,它会尝试将 String
值转换为 Int
值。但是,只有某些字符串可以转换成整数。字符串 "123"
可以转换成数值 123
,但字符串 "hello, world"
却没有对应的数值。下面的示例使用初始化器尝试将 String
转换为 Int
:
let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)
// convertedNumber 的类型是 "可选 Int"。
因为上面代码中的初始化器可能会失败,所以它返回的是可选 Int
,而不是 Int
。
要编写可选类型,需要在可选包含的类型名称后面加一个问号(?
)。例如,可选 Int
的类型是 Int?
。可选 Int
只能储存某个 Int
值或不储存任何值。它不能储存任何其他值,如 Bool
或 String
值。
1、nil
通过给可选变量赋特殊值 nil
,可以将其设置为无值状态:
var serverResponseCode: Int? = 404
// serverResponseCode 包含一个实际 Int 值 404
serverResponseCode = nil
// serverResponseCode 现在不包含任何值
如果你定义了一个可选变量,但没有提供默认值,那么该变量将自动设置为 nil
:
var surveyAnswer: String?
// surveyAnswer 自动设置为 nil
你可以使用 if
语句,通过比较可选和 nil 来确定可选是否包含一个值。你可以使用“等于”操作符(==
)或“不等于”操作符(!=
)进行比较。
如果可选有一个值,它就被视为“不等于” nil
:
let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)if convertedNumber != nil {print("convertedNumber contains some integer value.")
}
// 打印 "convertedNumber contains some integer value."
不能在非可选常量或变量中使用 nil
。如果代码中的常量或变量在某些条件下需要在没有值的情况下工作,请将其声明为适当类型的可选值。声明为非可选值的常量或变量保证永远不会包含 nil
值。如果尝试将 nil
赋值给一个非可选值,就会出现编译时错误。
通过将可选值和非可选值分开,可以显式标记哪些信息可能缺失,从而更方便编写处理缺失值的代码。你不能意外地将可选值当作非可选值来处理,因为这种错误会在编译时产生错误。在对值进行解包后,使用该值的其他代码都不需要检查 nil
,因此不需要在代码的不同部分重复检查同一个值。
在访问可选值时,代码总是同时处理 nil
和非 nil
两种情况。当值缺失时,可以执行如以下各节所述的几项操作:
- 当值为
nil
时,跳过对其进行操作的代码。 - 通过返回
nil
或使用 doc:OptionalChaining 中记述的?.
运算符传播nil
值。 - 使用
??
运算符提供一个后备值。 - 使用
!
运算符停止程序执行。
备注: 在 Objective-C 中,nil
是指向不存在对象的指针。在 Swift 中,nil
并非指针,而是特定类型值的缺失。任何类型的可选都可以被设置为 nil
,而不仅仅是对象类型。
2、可选绑定
你可以使用可选绑定来确定可选是否包含值,如果包含,则将该值作为临时常量或变量使用。可选绑定可与 if
、guard
和 while
语句一起使用,以检查可选中的值,并将该值提取到常量或变量中,作为单个操作的一部分。
使用 if
语句编写的可选绑定如下:
if let <#constantName#> = <#someOptional#> {<#statements#>
}
if let actualNumber = Int(possibleNumber) {print("The string \"\(possibleNumber)\" has an integer value of \(actualNumber)")
} else {print("The string \"\(possibleNumber)\" couldn't be converted to an integer")
}
// 打印 "The string "123" has an integer value of 123"
该代码可理解为:
“如果 Int(possibleNumber)
返回的可选 Int
中包含一个值,则将它赋值给名为 actualNumber
的新常量。”
如果转换成功,actualNumber
常量就可以在 if
语句的第一个分支中使用。这个常量已经用可选中的值进行了初始化,并具有相应的非可选类型。在本例中,possibleNumber
的类型是 Int?
,因此 actualNumber
的类型是 Int
。
如果在访问原可选常量或变量的值后不需要再引用它,则可以考虑使用相同的名称来命名新常量或变量:
let swift = Int(possibleNumber)
// 这里,myNumber 是一个可选整数
if let myNumber = myNumber {// 这里,myNumber 是一个非可选整数print("My number is \(myNumber)")
}
// 打印 "My number is 123"
这段代码首先检查 myNumber
是否包含一个值,就像上一个示例中的代码一样。如果 myNumber
有一个值,名为 myNumber
的新常量的值就会被设置为该值。在 if
语句的正文中,myNumber
指的就是这个新的非可选常量。在 if
语句之前或之后写 myNumber
,指的是原来的可选整数常量。
由于这种代码非常常见,因此可以使用更简短的语法来解包可选值:只写常量或变量的名称即可。解包后的新常量或变量隐式地使用与可选值相同的名称。
if let myNumber {print("My number is \(myNumber)")
}
// 打印 "My number is 123"
你可以在可选绑定时使用常量或变量。如果你想在 if
语句的第一个分支中修改 myNumber
的值,你可以改写为 if var myNumber
,这样,包含在可选中的值就可以作为变量而不是常量使用了。在 if
语句正文中对 myNumber
所做的修改仅适用于该局部变量,而不适用于原来的可选常量或变量。
你可以在一个 if
语句中包含任意数量的可选绑定和布尔条件,并用逗号分隔。如果可选绑定中的任何值为 nil
,或任何布尔条件的值为 false
,则整个 if
语句的条件被视为 false
。以下 if
语句是等价的:
if let firstNumber = Int("4"), let secondNumber = Int("42"), firstNumber < secondNumber && secondNumber < 100 {print("\(firstNumber) < \(secondNumber) < 100")
}
// 打印 "4 < 42 < 100"if let firstNumber = Int("4") {if let secondNumber = Int("42") {if firstNumber < secondNumber && secondNumber < 100 {print("\(firstNumber) < \(secondNumber) < 100")}}
}
// 打印 "4 < 42 < 100"
在 if
语句中使用可选绑定创建的常量和变量只能在 if
语句的正文中使用。与此相反,用 guard
语句创建的常量和变量仅在 guard
语句后的代码行中可用,如 doc:ControlFlow#Early-Exit 中所述。
3、提供后备值
处理缺失值的另一种方法是使用 nil-coalescing 操作符(??
)提供一个缺省值。如果 ??
左边的可选值不是 nil
,那么该值将被解包并使用。否则,将使用 ??
右侧的值。例如,如果指定了姓名,下面的代码会用姓名问候某人,如果姓名为 nil
,则使用通用问候语。
let name: String? = nil
let greeting = "Hello, " + (name ?? "friend") + "!"
print(greeting)
// 打印 "Hello, friend!"
4、强制解包
当 nil
表示不可恢复的故障时(如程序员错误或状态损坏),你可以通过在可选名称的末尾添加感叹号 (!
) 来访问底层值。这被称为强制解包可选的值。强制解包一个非 nil
值时,结果是其解包值。强制解包一个 nil
值则会引发运行时错误。
实际上,!
是 fatalError(_:file:line:)
的简写。例如,下面的代码显示了两种等效的方法:
let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)let number = convertedNumber!guard let number = convertedNumber else {fatalError("The number was invalid")
}
上述两个版本的代码都要求于 convertedNumber
始终包含一个值。使用上述任一方法将该要求写入代码,可让代码在运行时检查该要求是否为真。
4、隐式解包可选
如上所述,可选表示允许常量或变量“无值”。可以用 if
语句检查可选值是否存在,如果可选值确实存在,则可以通过可选绑定有条件地解除对可选值的包裹。
有时,从程序结构中可以清楚地看出,在首次设置可选值后,该可选将始终有一个值。在这种情况下,无需在每次访问可选时都对其值进行检查和解包,因为你可以安全地假定它一直都有值。
这类可选被定义为隐式解包可选。在编写隐式解包可选时,需要在可选类型后面加上感叹号(String!
)而不是问号(String?
)。要注意不是在使用可选时在其名称后加上感叹号,而是在声明可选时在其类型后加上感叹号。
当首次定义可选后,可选的值立即被确认存在,并且可以确保在此后的每一个时间点都存在值时,隐式解包可选就非常有用了。
当变量有可能在稍后阶段变为 nil
时,不要使用隐式解包可选。如果需要在变量的生命周期内检查变量是否为 nil
,请务必使用普通的可选类型。
隐式解包的可选在幕后是一个普通的可选值,但也可以像非可选值一样使用,而无需在每次访问时都进行解包。下面的示例显示了可选字符串和隐式解包的可选字符串在作为显式字符串访问其被包装值时的行为差异:
let possibleString: String? = "An optional string."
let forcedString: String = possibleString! // 需要显式解包let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString // 隐式解包
你可以将隐式解包可选视为允许可选值在需要时被强制解包。在使用隐式解包的可选值时,Swift 会首先尝试将其作为普通可选值使用;如果不能将其作为可选值使用,Swift 就会强制解包该值。在上面的代码中,可选值 assumedString
在赋值给 implicitString
之前被强制解包,因为 implicitString
的类型是显式定义的非可选字符串。在下面的代码中,optionalString
没有显式类型,所以它是一个普通的可选值。
let optionalString = assumedString
// optionalString 的类型是 "String?",而 assumedString 没有强制解包。
如果一个隐式解包的可选值为 nil
,而你试图访问它的被包装值,就会触发运行时错误。其结果与用感叹号来强制解包一个不包含值的普通可选完全相同。
你可以像检查普通可选一样,检查隐式解包的可选是否为 nil
:
if assumedString != nil {print(assumedString!)
}
// 打印 "An implicitly unwrapped optional string."
你也可以对隐式解包的可选使用可选绑定,在单个语句中检查并解包其值:
if let definiteString = assumedString {print(definiteString)
}
// 打印 "An implicitly unwrapped optional string."