2015-04-18 30 views
36

很多时候,你需要编写的代码如下所示:何时应该将可选值与nil进行比较?

if someOptional != nil { 
    // do something with the unwrapped someOptional e.g.  
    someFunction(someOptional!) 
} 

这似乎有点冗长,而且我听说使用!力展开运算符可以是不安全的,最好避免。有没有更好的方法来处理这个问题?

+1

详细?多年来一直在C,Java或C++中使用它们。 – Sulthan

+10

长寿和冗长是正交的 –

回答

93

几乎总是不需要检查是否可选不是nil。几乎是唯一需要这样做的时间是如果它的nil -ness只是只有你想知道的事情 - 你不在意价值是什么,只是它不是nil

在大多数其他情况下,有一些Swift速记可以更安全,更简洁地完成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将是本地副本 - 对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 

可以使用未缴合并运算符,??

// 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,或白灵菇(在雨燕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") 
    } 
} 

但它好得多通过保持错误处理代码顶部支票。这也会导致不愉快的嵌套(“厄运金字塔”)。

相反,可以使用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部分必须出口被保护值的范围内,例如一个returnfatalError,以保证保护值将在范围的其余部分有效。

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($0)}) { 
    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 

循环,直到函数返回nil

就像if let,你也可以写while let和循环,直到nil

while let line = readLine() { 
    print(line) 
} 

你也可以写while var(类似的注意事项if var apply)。

where条款也工作在这里(和终止循环,而不是跳过):

while let line = readLine() 
where !line.isEmpty { 
    print(line) 
} 

传递一个可选的到,需要一个非可选,并返回结果

除了函数作者:

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) } 

这是非常相似的可选链接,但对于当你需要非可选值传递到函数作为参数。与可选链接一样,结果将是可选的。

这是很好的,当你想要任意选择。例如,reduce1reduce类似,但使用第一个值作为种子,在数组为空时返回可选项。你可能会写这样的(用从早期的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) } 

但现在idxInt??型,双选。相反,你可以使用flatMap,其中“变平”的结果为单一可选:

let idx = fst.flatMap { find($0, 2) } 
// idx will be of type Int? 
// and not Int?? unlike if `map` was used 
+6

尽管你提到了零排序,构造“如果我<5 {}”可能是危险的,因为它很容易决定改变逻辑和写“if i> = 0 {} “而不是未来的编辑。就我个人而言,我试着总是使用“if let i = i where i <5 {}”with optionals。 –

+0

nil-coalescing operator,+1 – jithin

+0

Chris Eidhof在“Advanced Swift”中提到了很多这个问题,https://www.objc.io/books/advanced-swift/ –

0

您有一种方式。它被称为Optional Chaining。来自文件:

可选链是一个过程,用于查询和调用属性,方法和可能目前为零的可选下标。如果 可选包含值,则属性,方法或下标调用 成功;如果可选为零,则属性,方法或下标 调用返回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

0

我们可以使用可选的绑定。

var x:Int? 

if let y = x { 
    // x was not nil, and its value is now stored in y 
} 
else { 
    // x was nil 
} 
1

我想你应该回到Swift编程书籍,并了解这些东西的用途。 !当你确定可选项不为零时使用。既然你声明你是绝对肯定的,如果你错了,它会崩溃。这完全是故意的。这是“不安全和最好的避免”,因为在你的代码中断言是“不安全和最好的避免”。例如:

if someOptional != nil { 
    someFunction(someOptional!) 
} 

这个!绝对安全。除非有大的纰漏在你的代码,如误写(我希望你发现的bug)

if someOptional != nil { 
    someFunction(SomeOptional!) 
} 

在这种情况下,你的应用程序可能会崩溃,你调查为什么它崩溃了,你修正错误 - 这正是碰撞的原因。 Swift的一个目标是显然你的应用程序应该能够正常工作,但是由于Swift无法执行此操作,它会强制你的应用程序工作正常或者崩溃(如果可能),所以在应用程序发货之前会删除错误。

+1

这个自我回答主要是为了回答在问题中(甚至在一些流行的Swift教程书籍中)如果某些可选的!= nil {使用某些可选项}的不断出现。我也不同意使用'!'是一种很好的技术,当你想让你的程序在'nil'上崩溃时。你可能会更好地使用带有明确错误消息的'assert',而不是从'!'中得到的mystery-meat错误。 –

+2

可能这不是正确的,我在发布这个错误的东西,但肯定需要更多的可选问题的规范性答案,因为“我该怎么做“和类似的来_constantly。 –

+0

第二个示例很可能会导致编译器错误,而不是实际的应用程序崩溃。 –

相关问题