什么时候应该将可选值与 nil 进行比较?
When should I compare an optional value to nil?
很多时候,您需要编写如下代码:
if someOptional != nil {
// do something with the unwrapped someOptional e.g.
someFunction(someOptional!)
}
这似乎有点冗长,而且我听说使用 !
强制展开运算符可能不安全,最好避免使用。有没有更好的方法来处理这个问题?
几乎总是没有必要检查一个可选项是否不是 nil
。几乎唯一需要这样做的情况是,如果它的 nil
-ness 是 唯一 你想知道的事情——你不关心值是什么,只是它不是 nil
.
在大多数其他情况下,有一点Swift shorthand可以更安全简洁地为您完成if
中的任务。
如果不是nil
则使用该值
而不是:
let s = "1"
let i = Int(s)
if i != nil {
print(i! + 1)
}
你可以使用if let
:
if let i = Int(s) {
print(i + 1)
}
你也可以使用var
:
if var i = Int(s) {
print(++i) // prints 2
}
但请注意 i
将是 local 副本 - 对 i
的任何更改都不会影响原始可选内容中的值。
您可以在单个 if let
中解包多个可选,后面的可以依赖前面的:
if let url = NSURL(string: urlString),
data = NSData(contentsOfURL: url),
image = UIImage(data: data)
{
let view = UIImageView(image: image)
// etc.
}
您还可以向展开的值添加 where
子句:
if let url = NSURL(string: urlString) where url.pathExtension == "png",
let data = NSData(contentsOfURL: url), image = UIImage(data: data)
{ etc. }
将 nil
替换为默认值
而不是:
let j: Int
if i != nil {
j = i
}
else {
j = 0
}
或:
let j = i != nil ? i! : 0
您可以使用 nil-coalescing 运算符,??
:
// j will be the unwrapped value of i,
// or 0 if i is nil
let j = i ?? 0
将可选与非可选等同起来
而不是:
if i != nil && i! == 2 {
print("i is two and not nil")
}
您可以检查可选值是否等于非可选值:
if i == 2 {
print("i is two and not nil")
}
这也适用于比较:
if i < 5 { }
nil
始终等于其他 nil
,并且小于任何非 nil
值。
小心!这里可能有陷阱:
let a: Any = "hello"
let b: Any = "goodbye"
if (a as? Double) == (b as? Double) {
print("these will be equal because both nil...")
}
在可选的
上调用方法(或读取属性)
而不是:
let j: Int
if i != nil {
j = i.successor()
}
else {
// no reasonable action to take at this point
fatalError("no idea what to do now...")
}
您可以使用可选链接,?.
:
let j = i?.successor()
请注意,j
现在也将是可选的,以解决 fatalError
场景。稍后,您可以使用此答案中的其他技术之一来处理 j
的可选性,但您通常可以推迟实际解包您的可选值,直到很久以后,或者有时根本不解包。
顾名思义,你可以将它们链接起来,所以你可以这样写:
let j = s.toInt()?.successor()?.successor()
可选链接也适用于下标:
let dictOfArrays: ["nine": [0,1,2,3,4,5,6,7]]
let sevenOfNine = dictOfArrays["nine"]?[7] // returns {Some 7}
和功能:
let dictOfFuncs: [String:(Int,Int)->Int] = [
"add":(+),
"subtract":(-)
]
dictOfFuncs["add"]?(1,1) // returns {Some 2}
在可选的
上分配给 属性
而不是:
if splitViewController != nil {
splitViewController!.delegate = self
}
您可以通过分配一个可选链:
splitViewController?.delegate = self
只有当 splitViewController
不是 nil
时才会发生赋值。
如果不是 nil
或 bailing(Swift 2.0 中的新值)则使用该值
有时在一个函数中,你想写一小段代码来检查一个可选的,如果是nil
,就早点退出函数,否则继续。
你可以这样写:
func f(s: String) {
let i = Int(s)
if i == nil { fatalError("Input must be a number") }
print(i! + 1)
}
或者为了避免强制展开,像这样:
func f(s: String) {
if let i = Int(s) {
print(i! + 1)
}
else {
fatalErrr("Input must be a number")
}
}
但是通过检查将错误处理代码保留在顶部会更好。这也会导致不愉快的嵌套("pyramid of doom")。
您可以使用 guard
,这类似于 if not let
:
func f(s: String) {
guard let i = Int(s)
else { fatalError("Input must be a number") }
// i will be an non-optional Int
print(i+1)
}
else
部分必须退出保护值的范围,例如return
或 fatalError
,以保证受保护的值在范围的其余部分有效。
guard
不限于函数范围。例如以下内容:
var a = ["0","1","foo","2"]
while !a.isEmpty {
guard let i = Int(a.removeLast())
else { continue }
print(i+1, appendNewline: false)
}
打印 321
.
循环遍历序列中的非零项(Swift 2.0 中的新功能)
如果你有一个可选元素序列,你可以使用 for case let _?
遍历所有非可选元素:
let a = ["0","1","foo","2"]
for case let i? in a.map({ Int([=33=])}) {
print(i+1, appendNewline: false)
}
打印 321
。这是使用可选的模式匹配语法,这是一个变量名后跟 ?
.
您还可以在 switch
语句中使用此模式匹配:
func add(i: Int?, _ j: Int?) -> Int? {
switch (i,j) {
case (nil,nil), (_?,nil), (nil,_?):
return nil
case let (x?,y?):
return x + y
}
}
add(1,2) // 3
add(nil, 1) // nil
循环直到函数 returns nil
很像if let
,你也可以写while let
并循环直到nil
:
while let line = readLine() {
print(line)
}
您也可以写 while var
(与 if var
类似的注意事项适用)。
where
子句在这里也有效(并终止循环,而不是跳过):
while let line = readLine()
where !line.isEmpty {
print(line)
}
将可选参数传递给采用非可选参数且 return 结果为
的函数
而不是:
let j: Int
if i != nil {
j = abs(i!)
}
else {
// no reasonable action to take at this point
fatalError("no idea what to do now...")
}
您可以使用可选的 map
运算符:
let j = i.map { abs([=38=]) }
这与可选链接非常相似,但是当您需要将非可选值 传递给 函数作为参数时。与可选链接一样,结果将是可选的。
当你想要一个可选的时候,这很好。例如,reduce1
类似于 reduce
,但使用第一个值作为种子,return 为可选值以防数组为空。你可以这样写(使用前面的 guard
关键字):
extension Array {
func reduce1(combine: (T,T)->T)->T? {
guard let head = self.first
else { return nil }
return dropFirst(self).reduce(head, combine: combine)
}
}
[1,2,3].reduce1(+) // returns 6
但是您可以 map
.first
属性,并且 return 那:
extension Array {
func reduce1(combine: (T,T)->T)->T? {
return self.first.map {
dropFirst(self).reduce([=40=], combine: combine)
}
}
}
将可选参数传递给接受可选参数和 return 结果的函数,避免烦人的双重可选参数
有时,您想要类似于 map
的东西,但您要调用的函数 本身 return 是可选的。例如:
// an array of arrays
let arr = [[1,2,3],[4,5,6]]
// .first returns an optional of the first element of the array
// (optional because the array could be empty, in which case it's nil)
let fst = arr.first // fst is now [Int]?, an optional array of ints
// now, if we want to find the index of the value 2, we could use map and find
let idx = fst.map { find([=41=], 2) }
但是现在idx
是Int??
类型的,一个double-optional。相反,您可以使用 flatMap
,它将结果“扁平化”为一个可选的:
let idx = fst.flatMap { find([=42=], 2) }
// idx will be of type Int?
// and not Int?? unlike if `map` was used
你有一个办法。它被称为Optional Chaining。来自文档:
Optional chaining is a process for querying and calling properties,
methods, and subscripts on an optional that might currently be nil. If
the optional contains a value, the property, method, or subscript call
succeeds; if the optional is nil, the property, method, or subscript
call returns nil. Multiple queries can be chained together, and the
entire chain fails gracefully if any link in the chain is nil.
这是一些例子
class Person {
var residence: Residence?
}
class Residence {
var numberOfRooms = 1
}
let john = Person()
if let roomCount = john.residence?.numberOfRooms {
println("John's residence has \(roomCount) room(s).")
} else {
println("Unable to retrieve the number of rooms.")
}
// prints "Unable to retrieve the number of rooms."
您可以查看完整文章 here。
我们可以使用可选绑定。
var x:Int?
if let y = x {
// x was not nil, and its value is now stored in y
}
else {
// x was nil
}
我认为你应该回到 Swift 编程书籍并了解这些东西的用途。 !当您绝对确定可选项不为零时使用。因为你声明你绝对确定,所以如果你错了它会崩溃。这完全是故意的。从某种意义上说,它是 "unsafe and best avoided",您的代码中的断言是 "unsafe and best avoided"。例如:
if someOptional != nil {
someFunction(someOptional!)
}
!绝对安全。除非你的代码有很大的错误,比如写错了(希望你能发现错误)
if someOptional != nil {
someFunction(SomeOptional!)
}
在这种情况下,您的应用程序可能会崩溃,您调查它崩溃的原因,然后修复错误 - 这正是崩溃的原因。 Swift 中的一个目标显然是您的应用程序应该正常运行,但由于 Swift 无法强制执行此操作,它会强制您的应用程序正常运行或崩溃(如果可能),以便在应用程序发布之前消除错误。
经过大量的思考和研究,我想出了最简单的方法来打开一个可选的:
创建一个新的 Swift 文件并将其命名为 UnwrapOperator.swift
将以下代码粘贴到文件中:
import Foundation
import UIKit
protocol OptionalType { init() }
extension String: OptionalType {}
extension Int: OptionalType {}
extension Int64: OptionalType {}
extension Float: OptionalType {}
extension Double: OptionalType {}
extension CGFloat: OptionalType {}
extension Bool: OptionalType {}
extension UIImage : OptionalType {}
extension IndexPath : OptionalType {}
extension NSNumber : OptionalType {}
extension Date : OptionalType {}
extension UIViewController : OptionalType {}
postfix operator *?
postfix func *?<T: OptionalType>( lhs: T?) -> T {
guard let validLhs = lhs else { return T() }
return validLhs
}
prefix operator /
prefix func /<T: OptionalType>( rhs: T?) -> T {
guard let validRhs = rhs else { return T() }
return validRhs
}
现在上面的代码已经创建了2个运算符[一前缀一后缀]。
- 在解包时,您可以在可选项之前或之后使用这些运算符中的任何一个
解释很简单,运算符 returns 构造函数值 如果它们在变量中得到 nil,否则变量中包含的值。
下面是用法示例:
var a_optional : String? = "abc"
var b_optional : Int? = 123
// before the usage of Operators
print(a_optional) --> Optional("abc")
print(b_optional) --> Optional(123)
// Prefix Operator Usage
print(/a_optional) --> "abc"
print(/b_optional) --> 123
// Postfix Operator Usage
print(a_optional*?) --> "abc"
print(b_optional*?) --> 123
下面是 变量包含 nil 的例子:
var a_optional : String? = nil
var b_optional : Int? = nil
// before the usage of Operators
print(a_optional) --> nil
print(b_optional) --> nil
// Prefix Operator Usage
print(/a_optional) --> ""
print(/b_optional) --> 0
// Postfix Operator Usage
print(a_optional*?) --> ""
print(b_optional*?) --> 0
现在您可以选择使用哪个运算符,两者的用途相同。
很多时候,您需要编写如下代码:
if someOptional != nil {
// do something with the unwrapped someOptional e.g.
someFunction(someOptional!)
}
这似乎有点冗长,而且我听说使用 !
强制展开运算符可能不安全,最好避免使用。有没有更好的方法来处理这个问题?
几乎总是没有必要检查一个可选项是否不是 nil
。几乎唯一需要这样做的情况是,如果它的 nil
-ness 是 唯一 你想知道的事情——你不关心值是什么,只是它不是 nil
.
在大多数其他情况下,有一点Swift shorthand可以更安全简洁地为您完成if
中的任务。
如果不是nil
而不是:
let s = "1"
let i = Int(s)
if i != nil {
print(i! + 1)
}
你可以使用if let
:
if let i = Int(s) {
print(i + 1)
}
你也可以使用var
:
if var i = Int(s) {
print(++i) // prints 2
}
但请注意 i
将是 local 副本 - 对 i
的任何更改都不会影响原始可选内容中的值。
您可以在单个 if let
中解包多个可选,后面的可以依赖前面的:
if let url = NSURL(string: urlString),
data = NSData(contentsOfURL: url),
image = UIImage(data: data)
{
let view = UIImageView(image: image)
// etc.
}
您还可以向展开的值添加 where
子句:
if let url = NSURL(string: urlString) where url.pathExtension == "png",
let data = NSData(contentsOfURL: url), image = UIImage(data: data)
{ etc. }
将 nil
替换为默认值
而不是:
let j: Int
if i != nil {
j = i
}
else {
j = 0
}
或:
let j = i != nil ? i! : 0
您可以使用 nil-coalescing 运算符,??
:
// j will be the unwrapped value of i,
// or 0 if i is nil
let j = i ?? 0
将可选与非可选等同起来
而不是:
if i != nil && i! == 2 {
print("i is two and not nil")
}
您可以检查可选值是否等于非可选值:
if i == 2 {
print("i is two and not nil")
}
这也适用于比较:
if i < 5 { }
nil
始终等于其他 nil
,并且小于任何非 nil
值。
小心!这里可能有陷阱:
let a: Any = "hello"
let b: Any = "goodbye"
if (a as? Double) == (b as? Double) {
print("these will be equal because both nil...")
}
在可选的
上调用方法(或读取属性)而不是:
let j: Int
if i != nil {
j = i.successor()
}
else {
// no reasonable action to take at this point
fatalError("no idea what to do now...")
}
您可以使用可选链接,?.
:
let j = i?.successor()
请注意,j
现在也将是可选的,以解决 fatalError
场景。稍后,您可以使用此答案中的其他技术之一来处理 j
的可选性,但您通常可以推迟实际解包您的可选值,直到很久以后,或者有时根本不解包。
顾名思义,你可以将它们链接起来,所以你可以这样写:
let j = s.toInt()?.successor()?.successor()
可选链接也适用于下标:
let dictOfArrays: ["nine": [0,1,2,3,4,5,6,7]]
let sevenOfNine = dictOfArrays["nine"]?[7] // returns {Some 7}
和功能:
let dictOfFuncs: [String:(Int,Int)->Int] = [
"add":(+),
"subtract":(-)
]
dictOfFuncs["add"]?(1,1) // returns {Some 2}
在可选的
上分配给 属性而不是:
if splitViewController != nil {
splitViewController!.delegate = self
}
您可以通过分配一个可选链:
splitViewController?.delegate = self
只有当 splitViewController
不是 nil
时才会发生赋值。
如果不是 nil
或 bailing(Swift 2.0 中的新值)则使用该值
有时在一个函数中,你想写一小段代码来检查一个可选的,如果是nil
,就早点退出函数,否则继续。
你可以这样写:
func f(s: String) {
let i = Int(s)
if i == nil { fatalError("Input must be a number") }
print(i! + 1)
}
或者为了避免强制展开,像这样:
func f(s: String) {
if let i = Int(s) {
print(i! + 1)
}
else {
fatalErrr("Input must be a number")
}
}
但是通过检查将错误处理代码保留在顶部会更好。这也会导致不愉快的嵌套("pyramid of doom")。
您可以使用 guard
,这类似于 if not let
:
func f(s: String) {
guard let i = Int(s)
else { fatalError("Input must be a number") }
// i will be an non-optional Int
print(i+1)
}
else
部分必须退出保护值的范围,例如return
或 fatalError
,以保证受保护的值在范围的其余部分有效。
guard
不限于函数范围。例如以下内容:
var a = ["0","1","foo","2"]
while !a.isEmpty {
guard let i = Int(a.removeLast())
else { continue }
print(i+1, appendNewline: false)
}
打印 321
.
循环遍历序列中的非零项(Swift 2.0 中的新功能)
如果你有一个可选元素序列,你可以使用 for case let _?
遍历所有非可选元素:
let a = ["0","1","foo","2"]
for case let i? in a.map({ Int([=33=])}) {
print(i+1, appendNewline: false)
}
打印 321
。这是使用可选的模式匹配语法,这是一个变量名后跟 ?
.
您还可以在 switch
语句中使用此模式匹配:
func add(i: Int?, _ j: Int?) -> Int? {
switch (i,j) {
case (nil,nil), (_?,nil), (nil,_?):
return nil
case let (x?,y?):
return x + y
}
}
add(1,2) // 3
add(nil, 1) // nil
循环直到函数 returns nil
很像if let
,你也可以写while let
并循环直到nil
:
while let line = readLine() {
print(line)
}
您也可以写 while var
(与 if var
类似的注意事项适用)。
where
子句在这里也有效(并终止循环,而不是跳过):
while let line = readLine()
where !line.isEmpty {
print(line)
}
将可选参数传递给采用非可选参数且 return 结果为
的函数而不是:
let j: Int
if i != nil {
j = abs(i!)
}
else {
// no reasonable action to take at this point
fatalError("no idea what to do now...")
}
您可以使用可选的 map
运算符:
let j = i.map { abs([=38=]) }
这与可选链接非常相似,但是当您需要将非可选值 传递给 函数作为参数时。与可选链接一样,结果将是可选的。
当你想要一个可选的时候,这很好。例如,reduce1
类似于 reduce
,但使用第一个值作为种子,return 为可选值以防数组为空。你可以这样写(使用前面的 guard
关键字):
extension Array {
func reduce1(combine: (T,T)->T)->T? {
guard let head = self.first
else { return nil }
return dropFirst(self).reduce(head, combine: combine)
}
}
[1,2,3].reduce1(+) // returns 6
但是您可以 map
.first
属性,并且 return 那:
extension Array {
func reduce1(combine: (T,T)->T)->T? {
return self.first.map {
dropFirst(self).reduce([=40=], combine: combine)
}
}
}
将可选参数传递给接受可选参数和 return 结果的函数,避免烦人的双重可选参数
有时,您想要类似于 map
的东西,但您要调用的函数 本身 return 是可选的。例如:
// an array of arrays
let arr = [[1,2,3],[4,5,6]]
// .first returns an optional of the first element of the array
// (optional because the array could be empty, in which case it's nil)
let fst = arr.first // fst is now [Int]?, an optional array of ints
// now, if we want to find the index of the value 2, we could use map and find
let idx = fst.map { find([=41=], 2) }
但是现在idx
是Int??
类型的,一个double-optional。相反,您可以使用 flatMap
,它将结果“扁平化”为一个可选的:
let idx = fst.flatMap { find([=42=], 2) }
// idx will be of type Int?
// and not Int?? unlike if `map` was used
你有一个办法。它被称为Optional Chaining。来自文档:
Optional chaining is a process for querying and calling properties, methods, and subscripts on an optional that might currently be nil. If the optional contains a value, the property, method, or subscript call succeeds; if the optional is nil, the property, method, or subscript call returns nil. Multiple queries can be chained together, and the entire chain fails gracefully if any link in the chain is nil.
这是一些例子
class Person {
var residence: Residence?
}
class Residence {
var numberOfRooms = 1
}
let john = Person()
if let roomCount = john.residence?.numberOfRooms {
println("John's residence has \(roomCount) room(s).")
} else {
println("Unable to retrieve the number of rooms.")
}
// prints "Unable to retrieve the number of rooms."
您可以查看完整文章 here。
我们可以使用可选绑定。
var x:Int?
if let y = x {
// x was not nil, and its value is now stored in y
}
else {
// x was nil
}
我认为你应该回到 Swift 编程书籍并了解这些东西的用途。 !当您绝对确定可选项不为零时使用。因为你声明你绝对确定,所以如果你错了它会崩溃。这完全是故意的。从某种意义上说,它是 "unsafe and best avoided",您的代码中的断言是 "unsafe and best avoided"。例如:
if someOptional != nil {
someFunction(someOptional!)
}
!绝对安全。除非你的代码有很大的错误,比如写错了(希望你能发现错误)
if someOptional != nil {
someFunction(SomeOptional!)
}
在这种情况下,您的应用程序可能会崩溃,您调查它崩溃的原因,然后修复错误 - 这正是崩溃的原因。 Swift 中的一个目标显然是您的应用程序应该正常运行,但由于 Swift 无法强制执行此操作,它会强制您的应用程序正常运行或崩溃(如果可能),以便在应用程序发布之前消除错误。
经过大量的思考和研究,我想出了最简单的方法来打开一个可选的:
创建一个新的 Swift 文件并将其命名为 UnwrapOperator.swift
将以下代码粘贴到文件中:
import Foundation import UIKit protocol OptionalType { init() } extension String: OptionalType {} extension Int: OptionalType {} extension Int64: OptionalType {} extension Float: OptionalType {} extension Double: OptionalType {} extension CGFloat: OptionalType {} extension Bool: OptionalType {} extension UIImage : OptionalType {} extension IndexPath : OptionalType {} extension NSNumber : OptionalType {} extension Date : OptionalType {} extension UIViewController : OptionalType {} postfix operator *? postfix func *?<T: OptionalType>( lhs: T?) -> T { guard let validLhs = lhs else { return T() } return validLhs } prefix operator / prefix func /<T: OptionalType>( rhs: T?) -> T { guard let validRhs = rhs else { return T() } return validRhs }
现在上面的代码已经创建了2个运算符[一前缀一后缀]。
- 在解包时,您可以在可选项之前或之后使用这些运算符中的任何一个
解释很简单,运算符 returns 构造函数值 如果它们在变量中得到 nil,否则变量中包含的值。
下面是用法示例:
var a_optional : String? = "abc" var b_optional : Int? = 123 // before the usage of Operators print(a_optional) --> Optional("abc") print(b_optional) --> Optional(123) // Prefix Operator Usage print(/a_optional) --> "abc" print(/b_optional) --> 123 // Postfix Operator Usage print(a_optional*?) --> "abc" print(b_optional*?) --> 123
下面是 变量包含 nil 的例子:
var a_optional : String? = nil var b_optional : Int? = nil // before the usage of Operators print(a_optional) --> nil print(b_optional) --> nil // Prefix Operator Usage print(/a_optional) --> "" print(/b_optional) --> 0 // Postfix Operator Usage print(a_optional*?) --> "" print(b_optional*?) --> 0
现在您可以选择使用哪个运算符,两者的用途相同。