我什么时候应该将可选值与nil进行比较?

编程入门 行业动态 更新时间:2024-10-09 17:19:29
本文介绍了我什么时候应该将可选值与nil进行比较?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述

通常,您需要编写如下代码:

if someOptional!= nil { //用解开的someOptional做一些事情,例如 someFunction(someOptional!)}

这看起来有点冗长,我也听说使用!强制解包操作符可能不安全,最好避免使用。有没有更好的方法来处理这个?

解决方案

几乎总是没有必要检查一个可选项是否不是零。几乎是你唯一需要这样做的时候,如果它的 nil -ness是你想知道的唯一的东西 - 你没有关心价值是什么,只是它不是 nil 。

在大多数其他情况下,有一点Swift简写,可以更安全,更简洁地为您执行中的任务,如果。

使用价值如果不是 nil

而不是:

让s =1让i = Int(s) 如果i!= nil {打印(i!+ 1)}

您可以使用 if let :

if let i = Int(s){ print( i + 1)}

您还可以使用 var :

如果var i = Int(s){ print(++ i) //打印2 }

但请注意 i 将是本地副本 - 一个y更改为 i 不会影响原始可选内的值。

您可以在单个内容中打开多个选项如果让,后来的那些可以依赖于之前的那些:

if let url = NSURL(string:urlString), data = NSData(contentsOfURL:url), image = UIImage(data:data) { let view = UIImageView(image :image) //等}

您还可以添加 where 子句包含未展开的值:

如果让url = NSURL(字符串) :urlString)其中url.pathExtension ==png, let data = NSData(contentsOfURL:url),image = UIImage(data:data) {etc.}

使用默认值

而不是:

让j:Int if i! = nil {j = i } else {j = 0 }

或:

让j = i!= nil?一世! :0

你可以使用nil-coalescing运算符, ?? :

// j将是i的解包值, //或0如果我是零让j = i ?? 0

将可选项与非可选项等同

而不是:

如果i!= nil&&一世! == 2 {打印(我是两个而不是零)}

你可以检查选项是否等于非可选值:

if i == 2 { print(我是两个而不是零)}

这也适用于比较:

如果我< 5 {}

nil 总是等于其他 nil s,并且小于任何非 nil 值。

小心!这里可能有问题:

让a:Any =hello让b:Any =goodbye if(a as?Double)==(b as?Double){ print(这些将是相等的,因为两者都是......)}

在可选

上调用方法(或阅读属性)

而不是:

让j:Int if i!= nil {j = i.successor()} else { //此时没有采取合理的行动 fatalError(不知道现在要做什么...... )}

你可以使用可选链接,?。:

让j = i?.successor()

注意, j 现在也是可选的,以计入 fatalError 场景。之后,您可以使用此答案中的其他技巧之一来处理 j 的可选性,但您通常可以推迟实际解开您的选项,直到很久以后,或者有时不会所有。

顾名思义,您可以将它们链接起来,这样您就可以写下:

让j = s.toInt()?. successor()?. successor()

可选链接也适用于下标:

let dictOfArrays:[nine:[0,1,2,3 ,4,5,6,7]] 让sevenOfNine = dictOfArrays [九]?[7] //返回{Some 7}

和函数:

设dictOfFuncs:[String:(Int,Int ) - > Int] = [add:( +),减去:( - )] dictOfFuncs [add] ?(1,1)//返回{Some 2}

分配给某个属性一个可选的

而不是:

如果是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(输入必须是数字)} 打印(我! + 1)}

或避免强行解包,如下所示:

func f(s:String){ if let i = Int(s){ print(i! + 1)} else { fatalErrr(输入必须是数字)} }

但是通过检查将错误处理代码保持在顶部会更好。这也可能导致令人不快的嵌套(厄运金字塔)。

相反,你可以使用 guard ,这就像如果没有让:

func f(s:字符串){ guard let i = Int(s) else {fatalError(输入必须是数字)} //我将是一个非可选的Int 打印(i + 1)}

else part 必须退出保护值的范围,例如a 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中的新项目)

如果你有一系列的选项,你可以使用表示让_?迭代所有非可选元素:

让a = [0,1,foo,2] for case let i?在a.map({Int($ 0)}){ print(i + 1,appendNewline:false)}

打印 321 。这是使用模式匹配语法的可选项,它是一个变量名,后跟?。

你也可以在开关语句中使用此模式匹配:

func add( i:Int?,_ j:Int?) - >诠释? { switch(i,j){ case(nil,nil),(_?,nil),(nil,_?): return nil case let( x?,y?):返回x + y } } add(1,2)// 3 add(nil, 1)// nil

循环直到函数返回 nil

很像如果让,你也可以写 while 并循环直到 nil :

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

你也可以写而var (如果var 适用于,则类似注意事项)。

其中子句也在这里工作(并终止循环,而不是跳过):

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

将一个可选项传递给一个非可选函数并返回结果的函数

而不是:

让j:Int if i!= nil {j = abs(i!)} else { //此时没有采取合理的行动 fatalError(不知道现在该做什么......)}

你可以使用可选的 map 运算符:

让j = i.map {abs($ 0) }

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

当你想要一个可选的时候这很好。例如, 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(+)//返回6

但您可以映射 .first 属性,并返回:

extension Array { func reduce1(combine:(T,T) - > T) - > T ? {返回self.first.map { dropFirst(self).reduce($ 0,combine:combine)} } }

将可选项传递给一个带可选项并返回结果的函数,避免烦人的双重选项

有时候,你想要类似于 map 的东西,但你想要调用本身的函数返回一个可选项。例如:

//数组数组 let arr = [[1,2,3],[ 4,5,6]] // .first返回数组 //的第一个元素的可选项(可选,因为数组可能为空,在这种情况下它是nil)让fst = arr.first // fst现在是[Int] ?,一个可选的ints数组 //现在,如果我们想找到值2的索引,我们可以使用map并找到让idx = fst.map {find($ 0,2)}

但现在 idx 的类型为 Int ?? ,双重可选。相反,您可以使用 flatMap ,将结果展平为一个可选项:

让idx = fst.flatMap {find($ 0,2)} // idx将是Int类型? //而不是Int ??不像是`map`使用

Quite often, you need to write code such as the following:

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

This seems a bit verbose, and also I hear that using the ! force unwrap operator can be unsafe and best avoided. Is there a better way to handle this?

解决方案

It is almost always unnecessary to check if an optional is not nil. Pretty much the only time you need to do this is if its nil-ness is the only thing you want to know about – you don’t care what’s in the value, just that it’s not nil.

Under most other circumstances, there is a bit of Swift shorthand that can more safely and concisely do the task inside the if for you.

Using the value if it isn’t nil

Instead of:

let s = "1" let i = Int(s) if i != nil { print(i! + 1) }

you can use if let:

if let i = Int(s) { print(i + 1) }

You can also use var:

if var i = Int(s) { print(++i) // prints 2 }

but note that i will be a local copy - any changes to i will not affect the value inside the original optional.

You can unwrap multiple optionals within a single if let, and later ones can depend on earlier ones:

if let url = NSURL(string: urlString), data = NSData(contentsOfURL: url), image = UIImage(data: data) { let view = UIImageView(image: image) // etc. }

You can also add where clauses to the unwrapped values:

if let url = NSURL(string: urlString) where url.pathExtension == "png", let data = NSData(contentsOfURL: url), image = UIImage(data: data) { etc. }

Replacing nil with a default

Instead of:

let j: Int if i != nil { j = i } else { j = 0 }

or:

let j = i != nil ? i! : 0

you can use the nil-coalescing operator, ??:

// j will be the unwrapped value of i, // or 0 if i is nil let j = i ?? 0

Equating an optional with a non-optional

Instead of:

if i != nil && i! == 2 { print("i is two and not nil") }

you can check if optionals are equal to non-optional values:

if i == 2 { print("i is two and not nil") }

This also works with comparisons:

if i < 5 { }

nil is always equal to other nils, and is less than any non-nil value.

Be careful! There can be gotchas here:

let a: Any = "hello" let b: Any = "goodbye" if (a as? Double) == (b as? Double) { print("these will be equal because both nil...") }

Calling a method (or reading a property) on an optional

Instead of:

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

you can use optional chaining, ?.:

let j = i?.successor()

Note, j will also now be optional, to account for the fatalError scenario. Later, you can use one of the other techniques in this answer to handle j’s optionality, but you can often defer actually unwrapping your optionals until much later, or sometimes not at all.

As the name implies, you can chain them, so you can write:

let j = s.toInt()?.successor()?.successor()

Optional chaining also works with subscripts:

let dictOfArrays: ["nine": [0,1,2,3,4,5,6,7]] let sevenOfNine = dictOfArrays["nine"]?[7] // returns {Some 7}

and functions:

let dictOfFuncs: [String:(Int,Int)->Int] = [ "add":(+), "subtract":(-) ] dictOfFuncs["add"]?(1,1) // returns {Some 2}

Assigning to a property on an optional

Instead of:

if splitViewController != nil { splitViewController!.delegate = self }

you can assign through an optional chain:

splitViewController?.delegate = self

Only if splitViewController is non-nil will the assignment happen.

Using the value if it isn’t nil, or bailing (new in Swift 2.0)

Sometimes in a function, there’s a short bit of code you want to write to check an optional, and if it’s nil, exit the function early, otherwise keep going.

You might write this like this:

func f(s: String) { let i = Int(s) if i == nil { fatalError("Input must be a number") } print(i! + 1) }

or to avoid the force unwrap, like this:

func f(s: String) { if let i = Int(s) { print(i! + 1) } else { fatalErrr("Input must be a number") } }

but it’s much nicer to keep the error-handling code at the top by the check. This can also lead to unpleasant nesting (the "pyramid of doom").

Instead you can use guard, which is like an 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) }

The else part must exit the scope of the guarded value, e.g. a return or fatalError, to guarantee that the guarded value will be valid for the remainder of the scope.

guard isn’t limited to function scope. For example the following:

var a = ["0","1","foo","2"] while !a.isEmpty { guard let i = Int(a.removeLast()) else { continue } print(i+1, appendNewline: false) }

prints 321.

Looping over non-nil items in a sequence (new in Swift 2.0)

If you have a sequence of optionals, you can use for case let _? to iterate over all the non-optional elements:

let a = ["0","1","foo","2"] for case let i? in a.map({ Int($0)}) { print(i+1, appendNewline: false) }

prints 321. This is using the pattern-matching syntax for an optional, which is a variable name followed by ?.

You can also use this pattern matching in switch statements:

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

Looping until a function returns nil

Much like if let, you can also write while let and loop until nil:

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

You can also write while var (similar caveats to if var apply).

where clauses also work here (and terminate the loop, rather than skipping):

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

Passing an optional into a function that takes a non-optional and returns a result

Instead of:

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

you can use optional’s map operator:

let j = i.map { abs($0) }

This is very similar to optional chaining, but for when you need to pass the non-optional value into the function as an argument. As with optional chaining, the result will be optional.

This is nice when you want an optional anyway. For example, reduce1 is like reduce, but uses the first value as the seed, returning an optional in case the array is empty. You might write it like this (using the guard keyword from earlier):

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

But instead you could map the .first property, and return that:

extension Array { func reduce1(combine: (T,T)->T)->T? { return self.first.map { dropFirst(self).reduce($0, combine: combine) } } }

Passing an optional into a function that takes an optional and returns a result, avoiding annoying double-optionals

Sometimes, you want something similar to map, but the function you want to call itself returns an optional. For example:

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

But now idx is of type Int??, a double-optional. Instead, you can use flatMap, which "flattens" the result into a single optional:

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

更多推荐

我什么时候应该将可选值与nil进行比较?

本文发布于:2023-11-30 09:54:01,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1649563.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:什么时候   可选   nil

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!