简体   繁体   English

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

[英]When should I compare an optional value to nil?

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 . 几乎总是不必检查可选项是否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 . 几乎你需要做的这唯一的一次是,如果它的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 local copy - 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多个optionals,而后者可以依赖于之前的那些:

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未包装的值中添加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秒,小于任何非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. 注意, j现在也是可选的,以解决fatalError场景。 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的可选性,但是您可以经常推迟实际解开您的选项,直到很久以后,或者有时根本不打开。

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. 只有当splitViewControllernil才会进行赋值。

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 must exit the scope of the guarded value, eg a return or fatalError , to guarantee that the guarded value will be valid for the remainder of the scope. else部分必须退出保护值的范围,例如returnfatalError ,以保证保护值对范围的其余部分有效。

guard isn't limited to function scope. guard不仅限于功能范围。 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 . 打印321

Looping over non-nil items in a sequence (new in Swift 2.0) 循环遍历序列中的非零项(Swift 2.0中的新项)

If you have a sequence of optionals, you can use for case let _? 如果你有一系列的选项,你可以使用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 . 打印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: 您还可以在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

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() {
    print(line)
}

You can also write while var (similar caveats to if var apply). 您也可以while var写入( if var适用if var类似注意事项)。

where clauses also work here (and terminate the loop, rather than skipping): where子句也在这里工作(并终止循环,而不是跳过):

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: 你可以使用optional的map运算符:

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. 例如, reduce1类似于reduce ,但使用第一个值作为种子,如果数组为空则返回可选项。 You might write it like this (using the guard keyword from earlier): 您可以这样写(使用之前的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

But instead you could map the .first property, and return that: 但是你可以map .first属性,并返回:

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. 有时候,你想要一些类似于map东西,但是你想要调用的函数本身会返回一个可选的。 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?? 但现在idx的类型是Int?? , a double-optional. ,双重选择。 Instead, you can use flatMap , which “flattens” the result into a single optional: 相反,您可以使用flatMap ,将结果“展平”为单个可选:

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

I think you should go back to the Swift programming book and learn what these things are for. 我想你应该回到Swift编程书中,了解这些内容的用途。 ! is used when you are absolutely sure that the optional isn't nil. 当你绝对确定可选项不是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: 例如:

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

The ! 的! is absolutely safe. 绝对安全。 Unless there is a big blunder in your code, like writing by mistake (I hope you spot the bug) 除非您的代码中存在重大错误,例如错误地写错(我希望您发现错误)

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

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无法强制执行此操作,它会强制您的应用程序正常工作或崩溃(如果可能),因此在应用程序发布之前会删除错误。

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. 可选链接是一个查询和调用当前可能为nil的可选项的属性,方法和下标的过程。 If the optional contains a value, the property, method, or subscript call succeeds; 如果optional包含值,则属性,方法或下标调用成功; if the optional is nil, the property, method, or subscript call returns nil. 如果optional是nil,则属性,方法或下标调用返回nil。 Multiple queries can be chained together, and the entire chain fails gracefully if any link in the chain is nil. 多个查询可以链接在一起,如果链中的任何链接为零,整个链都会正常失败。

Here is some example 这是一些例子

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

You can check the full article here . 你可以在这里查看完整的文章。

We can use optional binding. 我们可以使用可选绑定。

var x:Int?

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

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 创建一个新的Swift文件并将其命名为UnwrapOperator.swift

  • Paste the following code in the file : 将以下代码粘贴到文件中:

     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 } 
  • Now the above code has created 2 operator [One prefix and one postfix]. 现在上面的代码创建了2个运算符[一个前缀和一个后缀]。

  • 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 value if they get nil in variable else the contained value inside the variable. 解释很简单,如果操作符在变量中得到nil,则返回构造函数值,否则返回变量中包含的值。

  • Below is the example of usage : 以下是用法示例:

     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 
  • Below is the example when variable contains nil : 下面是变量包含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 
  • Now it is your choice which operator you use, both serve the same purpose. 现在您可以选择使用哪种运营商,两者都有相同的用途。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 展开Optional值时意外发现nil - Unexpectedly found nil when unwrapping an Optional value 在Swift中,我应该在完成后将可选实例变量设置为nil吗? - In Swift should I set my optional instance variables to nil when done? 发现 nil 时崩溃,同时隐式展开非 nil 的可选值 - Crash when found nil while implicitly unwrapping an Optional value that is not nil 当我实现Parse时,我得到“致命错误:在展开可选值时意外发现nil” - When I implement Parse I get “fatal error: unexpectedly found nil while unwrapping an Optional value” 遇到:展开一个Optional值时意外发现nil,当我尝试使其变为可选值时,出现另一个错误 - Running into: Unexpectedly found nil while unwrapping an Optional value, when I try to make it optional, I get another error Segue可选值nil - Segue Optional Value nil 解包可选的 IBOutlet 时出现错误(线程 1:致命错误:解包可选值时意外发现 nil) - I have an error when unwrapping an optional IBOutlet (Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value) 当我尝试通过AVAssetImageGenerator获取CGImage时,“在展开可选值时意外发现nil” - “unexpectedly found nil while unwrapping an Optional value” when I try to get a CGImage by AVAssetImageGenerator 展开可选值时,UiCollectionView单元意外发现为零 - UiCollectionView Cell unexpectedly found nil when unwrapping optional value SpriteKit SKCameraNode - 在分配后解包可选值时为Nil - SpriteKit SKCameraNode - Nil when unwrapping an optional value just after assigned
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM