【问题标题】:What's the difference between if nil != optional … and if let _ = optional …if nil != optional ... 和 if let _ = optional ... 有什么区别?
【发布时间】:2015-03-28 21:44:32
【问题描述】:

我需要测试返回可选值的表达式是否为nil。这看起来很简单,但这里是代码。

if nil != self?.checklists.itemPassingTest({ $0 === note.object }) {
    …
}

出于某种原因,这让我觉得很不舒服。

if let item = self?.checklists.itemPassingTest({ $0 === note.object }) {
    …
}

对我来说看起来好多了,但我实际上并不需要该物品,我只需要知道是否有退货。所以,我使用了以下内容。

if let _ = self?.checklists.itemPassingTest({ $0 === note.object }) {
    …
}

我在这里错过了什么微妙的东西吗?我认为if nil != optional …if let _ = optional … 在这里是等价的。


更新以解决答案中的一些问题

  1. 我看不出nil != varvar != nil 之间的区别,尽管我通常使用var != nil。在这种情况下,在块获得块的布尔比较与 if 的布尔比较混合后推送!= nil

  2. Wildcard Pattern 的使用不应该是那么令人惊讶或不常见。它们用于元组(x, _) = (10, 20)、for-in 循环for _ in 1...5、case 语句case (_, 0): 等(注意:这些示例取自The Swift Programming Language)。

这个问题是关于两种形式的功能等效性,而不是关于编码风格的选择。可以在programmers.stackexchange.com 上进行对话。


经过这么长时间,Swift 2.0 让它变得毫无意义

if self?.checklists.contains({ $0 === note.object }) ?? false {
    …
}

【问题讨论】:

    标签: swift optional


    【解决方案1】:

    经过优化,这两种方式大概是一样的。

    例如,在这种情况下,使用swiftc -O -emit-assembly if_let.swift 编译以下两个:

    import Darwin
    
    // using arc4random ensures -O doesn’t just
    // ignore your if statement completely
    let i: Int? = arc4random()%2 == 0 ? 2 : nil
    
    if i != nil {
      println("set!")
    }
    

    import Darwin
    
    let i: Int? = arc4random()%2 == 0 ? 2 : nil
    
    if let _ = i {
      println("set!")
    }
    

    产生相同的汇编代码:

        ; call to arc4random
        callq   _arc4random
        ; check if LSB == 1 
        testb   $1, %al
        ; if it is, skip the println
        je  LBB0_1
        movq    $0, __Tv6if_let1iGSqSi_(%rip)
        movb    $1, __Tv6if_let1iGSqSi_+8(%rip)
        jmp LBB0_3
    LBB0_1:
        movq    $2, __Tv6if_let1iGSqSi_(%rip)
        movb    $0, __Tv6if_let1iGSqSi_+8(%rip)
        leaq    L___unnamed_1(%rip), %rax  ; address of "set!" literal
        movq    %rax, -40(%rbp)
        movq    $4, -32(%rbp)
        movq    $0, -24(%rbp)
        movq    __TMdSS@GOTPCREL(%rip), %rsi
        addq    $8, %rsi
        leaq    -40(%rbp), %rdi
        ; call println
        callq   __TFSs7printlnU__FQ_T_
    LBB0_3:
        xorl    %eax, %eax
        addq    $32, %rsp
        popq    %rbx
        popq    %r14
        popq    %rbp
        retq
    

    【讨论】:

    • func check (x:Int) { /* arc4random() -> x */ } 将输出从 1,000 页减少到 1.5 页。 :-)
    • 在这种情况下,我并不关心性能,但知道它们具有等效的二进制表示形式让我放心。
    【解决方案2】:

    if let 语法称为可选绑定。它接受一个可选项作为输入,如果可选项不为零,则返回一个必需的常量。这适用于常见的代码模式,您首先检查一个值是否为 nil,如果它不是 nil(如果它有一个值),则对它执行一些操作。

    如果可选的 nil,则处理停止并跳过大括号内的代码。

    if optional != nil 语法更简单。它只是检查可选项是否为零。它会跳过为您创建所需的常量。

    如果您不打算使用结果值,则可选绑定语法既浪费又令人困惑。在这种情况下使用更简单的if optional != nil 版本。正如 nhgrif 指出的那样,它生成的代码更少,而且您的意图更加清晰。

    编辑:

    听起来编译器足够聪明,如果您编写“if let”可选绑定代码但最终没有使用您绑定的变量,则不会生成额外代码。主要区别在于可读性。使用可选绑定会产生您将使用您绑定的可选绑定的期望。

    【讨论】:

    • 考虑到 Apple 的 swift 示例中使用了 numbertimes for _ in rangenumber,我认为未绑定的变量应该不会那么令人惊讶。
    • 正如这张海报所说,正如我在上面的另一条评论中添加的那样,源代码是人类可读的代码,因此应该表达人类如何说出逻辑。大声朗读逻辑是说“如果这不是零,那么……”在源代码中完全转换为“如果 x != nil {……”,这意味着更具可读性。老实说,我完全没有看到'if let _ = x' 的好处。也许这只是当您不关心一个或多个值时能够进行模式匹配的副产品,为了支持这一点,它最终创造了“if let _”废话。
    • for _ in range 是一个非常不同的情况。在这种情况下,您将循环运行特定次数,但不需要索引值。
    【解决方案3】:

    我个人认为这看起来很不愉快,因为您将 nil 与结果进行比较,而不是将结果与 nil 进行比较:

    if self?.checklists.itemPassingTest({ $0 === note.object }) != nil {
        …
    }
    

    由于您只想确保它不是 nil 并且不使用 item,因此使用 let 毫无意义。

    【讨论】:

      【解决方案4】:

      AirspeedVelocity 的答案告诉我们let _ =!= nil 产生相同的汇编代码,因此我强烈建议使用第一种方法。

      事实上,如果你有类似的东西:

      if let _ = optional {
          do_something()
      }
      

      ...你想添加一些代码,现在你需要那个可选的,这个改变会更容易和更快:

      if let wrapped = optional {
          do_something()
          do_something_else(with: wrapped)
      }
      

      使用let _ = 并编写可维护的代码。

      【讨论】:

      • 我正要投票赞成,但后来我想到源代码是什么......人类可读的代码。虽然您是对的,如果您需要该值,将来更容易更改,但从其他语法更改并不难。更重要的是,在那之前(如果有的话),它不那么可读。想一想……大声朗读逻辑是说“如果这不是零,那么……”在源代码中完全转换为“如果 x != nil {……”,这意味着更具可读性。这就是我改变主意的原因。
      猜你喜欢
      • 1970-01-01
      • 2022-11-27
      • 2015-08-12
      • 1970-01-01
      • 2011-03-25
      • 2013-11-18
      • 1970-01-01
      • 1970-01-01
      • 2011-03-20
      相关资源
      最近更新 更多