什么时候应该比较可选值为零?
很多时候,你需要编写如下的代码:
if someOptional != nil { // do something with the unwrapped someOptional eg someFunction(someOptional!) }
这似乎有点冗长,也听说用!
powershell解包运营商可能是不安全的,最好避免。 有没有更好的方法来处理这个问题?
几乎总是没有必要检查一个可选项是不是nil
。 你需要做的唯一一件事情就是如果你想知道的唯一事情就是你想知道的唯一的事情 – 你不在意价值是什么,只是它不是nil
。
在其他大多数情况下,有一些Swift速记可以更安全,更简洁地完成任务里面的任务。
如果不是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
将是本地副本 – 对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. }
用默认值replacenil
代替:
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") }
你可以检查optionals是否等于非可选值:
if i == 2 { print("i is two and not nil") }
这也适用于比较:
if i < 5 { }
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
的select性,但是你通常可以推迟实际上的select,直到很久以后,或者根本没有。
顾名思义,你可以链接它们,所以你可以写:
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}
和function:
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
,则使用该值或释放(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") } }
但是通过检查来保持error handling代码在顶部会更好。 这也可能导致不愉快的嵌套(“厄运金字塔”)。
相反,你可以使用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
不限于function范围。 例如以下内容:
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中是新的)
如果你有一系列的select权,你可以使用for case let _?
遍历所有非可选元素:
let a = ["0","1","foo","2"] for case let i? in a.map({ Int($0)}) { print(i+1, appendNewline: false) }
打印321
。 这是使用模式匹配语法的一个可选的,这是一个variables名称后跟?
。
你也可以在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
循环直到函数返回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) }
将可选parameter passing给一个非可选的函数并返回结果
代替:
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($0) }
这与可选的链接非常相似,但是当您需要将非可选值作为parameter passing给函数时。 与可选链接一样,结果将是可选的。
无论如何,这是很好的select。 例如, reduce1
就像reduce
,但是使用第一个值作为种子,在数组为空的情况下返回一个可选项。 你可以这样写(使用前面的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
属性,然后返回:
extension Array { func reduce1(combine: (T,T)->T)->T? { return self.first.map { dropFirst(self).reduce($0, combine: combine) } } }
将一个可选项传递给一个可选的函数并返回一个结果,避免恼人的双选项
有时候,你需要类似map
东西,但是你想调用的函数本身返回一个可选项。 例如:
// 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($0, 2) }
但是现在idx
的types是Int??
,双选可选。 相反,您可以使用flatMap
,它将结果“变平”为单个可选:
let idx = fst.flatMap { find($0, 2) } // idx will be of type Int? // and not Int?? unlike if `map` was used
我认为你应该回到Swift编程书籍,了解这些东西的用途。 ! 当你确定可选项不是零时使用。 既然你声明你是绝对肯定的,如果你错了,就会崩溃。 这完全是故意的。 这是“不安全和最好的避免”,因为在你的代码中断言是“不安全和最好的避免”。 例如:
if someOptional != nil { someFunction(someOptional!) }
那! 是绝对安全的。 除非你的代码有一个大的错误,比如写错了(我希望你发现错误)
if someOptional != nil { someFunction(SomeOptional!) }
在这种情况下,你的应用程序可能会崩溃,你调查为什么它崩溃,并修复了错误 – 这正是崩溃的原因。 Swift的一个目标是显然你的应用程序应该能够正常工作,但是由于Swift无法强制执行,所以它会强制你的应用程序工作正常,或者如果可能的话崩溃,所以在应用程序发货之前就会删除错误。
你有一个方法。 它被称为可选链 。 从文档:
可选链接是查询和调用可能当前为零的可选属性,方法和下标的过程。 如果可选包含一个值,那么property,method或subscript调用会成功; 如果可选为零,则property,method或subscript调用返回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."
你可以在这里查看完整的文章。
我们可以使用可选的绑定。
var x:Int? if let y = x { // x was not nil, and its value is now stored in y } else { // x was nil }