ios 我什么时候应该将可选值与 nil 进行比较?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/29717210/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
When should I compare an optional value to nil?
提问by Airspeed Velocity
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?
这似乎有点冗长,而且我听说使用!
force unwrap 运算符可能不安全,最好避免。有没有更好的方法来处理这个问题?
回答by Airspeed Velocity
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 onlything you want to know about – you don't care what's in the value, just that it's not nil
.
几乎总是没有必要检查可选项是否不是nil
。几乎唯一需要这样做的时候是,如果它的nil
-ness 是你唯一想知道的事情——你不关心值中有什么,只是它不是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.
在大多数其他情况下,有一些 Swift 速记可以更安全、更简洁地if
为您完成内部任务。
Using the value if it isn't nil
如果不是,则使用该值 nil
Instead of:
代替:
let s = "1"
let i = Int(s)
if i != nil {
print(i! + 1)
}
you can use if let
:
你可以使用if let
:
if let i = Int(s) {
print(i + 1)
}
You can also use var
:
您还可以使用var
:
if var i = Int(s) {
print(++i) // prints 2
}
but note that i
will be a localcopy - any changes to i
will not affect the value inside the original optional.
但请注意,这i
将是一个本地副本 - 任何更改i
都不会影响原始可选中的值。
You can unwrap multiple optionals within a single if let
, and later ones can depend on earlier ones:
您可以在单个 中解开多个选项if let
,后面的可以依赖于前面的:
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:
您还可以where
向展开的值添加子句:
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
替换nil
为默认值
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, ??
:
您可以使用 nil-coalescing 运算符,??
:
// 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 nil
s, and is less than any non-nil
value.
nil
总是等于其他nil
s,并且小于任何非nil
值。
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.
请注意,j
现在也将是可选的,以说明fatalError
场景。稍后,您可以使用此答案中的其他技术之一来处理j
的可选性,但您通常可以将实际展开您的可选性推迟到很晚,或者有时根本不展开。
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 throughan optional chain:
您可以通过可选链分配:
splitViewController?.delegate = self
Only if splitViewController
is non-nil
will the assignment happen.
只有当splitViewController
是 non- 时nil
才会发生分配。
Using the value if it isn't nil
, or bailing (new in Swift 2.0)
如果不是nil
,则使用该值,或者放弃(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.
有时在一个函数中,您想编写一小段代码来检查一个可选项,如果是nil
,则提前退出该函数,否则继续执行。
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
:
相反,您可以使用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)
}
The else
part mustexit 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.
该else
部分必须退出保护值的范围,例如 areturn
或fatalError
,以保证保护值对范围的其余部分有效。
guard
isn't limited to function scope. For example the following:
guard
不限于功能范围。例如以下内容:
var a = ["0","1","foo","2"]
while !a.isEmpty {
guard let i = Int(a.removeLast())
else { continue }
print(i+1, appendNewline: false)
}
prints 321
.
打印321
。
Looping over non-nil items in a sequence (new in Swift 2.0)
循环遍历序列中的非 nil 项目(Swift 2.0 中的新功能)
If you have a sequence of optionals, you can use for case let _?
to iterate over all the non-optional elements:
如果您有一系列for case let _?
可选元素,则可以使用它来遍历所有非可选元素:
let a = ["0","1","foo","2"]
for case let i? in a.map({ Int(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
)}) {
print(i+1, appendNewline: false)
}
prints 321
. This is using the pattern-matching syntax for an optional, which is a variable name followed by ?
.
打印321
。这是将模式匹配语法用于可选项,即变量名后跟?
.
You can also use this pattern matching in switch
statements:
您还可以在switch
语句中使用此模式匹配:
while let line = readLine() {
print(line)
}
Looping until a function returns nil
循环直到函数返回 nil
Much like if let
, you can also write while let
and loop until nil
:
很像if let
,您也可以编写while let
和循环直到nil
:
while let line = readLine()
where !line.isEmpty {
print(line)
}
You can also write while var
(similar caveats to if var
apply).
你也可以写while var
(类似的警告if var
适用)。
where
clauses also work here (and terminate the loop, rather than skipping):
where
子句在这里也有效(并终止循环,而不是跳过):
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...")
}
Passing an optional into a function that takes a non-optional and returns a result
将一个可选参数传递给一个接受非可选参数并返回结果的函数
Instead of:
代替:
let j = i.map { abs(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
) }
you can use optional's map
operator:
您可以使用 optional 的map
运算符:
extension Array {
func reduce1(combine: (T,T)->T)->T? {
return self.first.map {
dropFirst(self).reduce(// 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(let idx = fst.flatMap { find(if someOptional != nil {
someFunction(someOptional!)
}
, 2) }
// idx will be of type Int?
// and not Int?? unlike if `map` was used
, 2) }
, combine: combine)
}
}
}
This is very similar to optional chaining, but for when you need to pass the non-optional value intothe 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):
当你想要一个可选的时候,这很好。例如,reduce1
is like reduce
,但使用第一个值作为种子,如果数组为空,则返回一个可选值。你可以这样写(使用guard
前面的关键字):
if someOptional != nil {
someFunction(SomeOptional!)
}
But instead you could map
the .first
property, and return that:
但是,您可以使用map
该.first
属性,然后返回:
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
}
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 itselfreturns an optional. For example:
有时,您想要类似于 的东西map
,但您要调用的函数本身返回一个可选的。例如:
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
But now idx
is of type Int??
, a double-optional. Instead, you can use flatMap
, which “flattens” the result into a single optional:
但现在idx
是 type Int??
,一个双重可选的。相反,您可以使用flatMap
,它将结果“扁平化”为单个可选:
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
回答by gnasher729
I think you should go back to the Swift programming book and learn what these things are for. ! is used when you are absolutely sure that the optional isn't nil. Since you declared that you are absolutely sure, it crashes if you're wrong. Which is entirely intentional. It is "unsafe and best avoided" in the sense that asserts in your code are "unsafe and best avoided". For example:
我认为你应该回到 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
}
The ! is absolutely safe. Unless there is a big blunder in your code, like writing by mistake (I hope you spot the bug)
这 !绝对安全。除非你的代码有很大的错误,比如写错了(我希望你能发现错误)
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
in which case your app may crash, you investigate why it crashes, and you fix the bug - which is exactly what the crash is there for. One goal in Swift is that obviously your app should work correctly, but since Swift cannot enforce this, it enforces that your app either works correctly or crashes if possible, so bugs get removed before the app ships.
在这种情况下,您的应用程序可能会崩溃,您调查它崩溃的原因,然后修复错误 - 这正是崩溃的原因。Swift 的一个目标显然是您的应用程序应该正确运行,但由于 Swift 无法强制执行此操作,因此它会强制您的应用程序正常运行或在可能的情况下崩溃,因此在应用程序发布之前会删除错误。
回答by Mr. Bean
After lot of thinking and researching i have came up with the easiest way to unwrap an optional :
经过大量的思考和研究,我想出了最简单的方法来解开一个可选的:
Create a new Swift File and name it UnwrapOperator.swift
Paste the following code in the file :
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
Now the above code has created 2 operator [One prefix and one postfix].
- At the time of unwrapping you can use either of these operator before or after the optionals
The explanation is simple, the operators returns the constructor valueif they get nil in variable else the contained value inside the variable.
Below is the example of usage :
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."
Below is the example when variable contains nil:
var x:Int? if let y = x { // x was not nil, and its value is now stored in y } else { // x was nil }
Now it is your choice which operator you use, both serve the same purpose.
创建一个新的 Swift 文件并将其命名为 UnwrapOperator.swift
将以下代码粘贴到文件中:
##代码##现在上面的代码已经创建了 2 个操作符 [一前缀一后缀]。
- 在解包时,您可以在选项之前或之后使用这些运算符中的任何一个
解释很简单,如果运算符在变量中为 nil,则返回构造函数值,否则为变量中包含的值。
以下是使用示例:
##代码##以下是变量包含 nil时的示例:
##代码##现在您可以选择使用哪个运算符,两者都具有相同的目的。
回答by Sergey Pekar
You there is one way. It is called Optional Chaining. From documentation:
你有一种方法。它被称为可选链接。从文档:
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.
可选链是查询和调用当前可能为 nil 的可选的属性、方法和下标的过程。如果可选项包含值,则属性、方法或下标调用成功;如果可选项为 nil,则属性、方法或下标调用返回 nil。可以将多个查询链接在一起,如果链中的任何链接为零,则整个链都会优雅地失败。
Here is some example
这是一些例子
##代码##You can check the full article here.
您可以在此处查看全文。
回答by 13th Ghost
We can use optional binding.
我们可以使用可选绑定。
##代码##