Какая разница между if nil!= Optional... и if let _ = optional...

Мне нужно проверить, будет ли выражение, возвращающее необязательный, 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 … здесь эквивалентны.


Обновить для устранения некоторых проблем в ответах

  • Я не вижу разницы между nil != var и var != nil, хотя обычно использую var != nil. В этом случае нажатие != nil после блока получает логическое сравнение блока, смешанного с булевым сравнением if.

  • Использование шаблона подстановок не должно быть таким неожиданным или необычным. Они используются в кортежах (x, _) = (10, 20), for-in loops for _ in 1...5, case case case (_, 0): и т.д. (ПРИМЕЧАНИЕ: эти примеры взяты из языка Swift Programming).

Этот вопрос касается функциональной эквивалентности двух форм, а не выбора стиля кодирования. Этот разговор может быть сделан на programers.stackexchange.com.


После всего этого времени Swift 2.0 делает его спорным

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

Ответы

Ответ 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    [email protected](%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

Ответ 2

Синтаксис if let называется необязательным связыванием. Он принимает необязательный вход и возвращает требуемую константу, если необязательный параметр не равен нулю. Это предназначено для шаблона общего кода, в котором вы сначала проверяете, имеет ли значение значение nil, а если нет, вы что-то делаете с ним.

Если необязательный - nil, обработка останавливается и код внутри фигурных скобок пропускается.

Синтаксис if optional != nil проще. Он просто проверяет, не является ли опциональным значение nil. Он пропускает создание необходимой константы для вас.

Необязательный синтаксис связывания является расточительным и запутанным, если вы не собираетесь использовать полученное значение. Используйте более простой вариант if optional != nil в этом случае. Как указывает nhgrif, он генерирует меньше кода, плюс ваши намерения гораздо яснее.

EDIT:

Похоже, что компилятор достаточно умен, чтобы не генерировать дополнительный код, если вы пишете необязательный код привязки, если не хотите, но не используете переменную, которую вы связываете. Основное различие в читаемости. Использование необязательной привязки создает ожидание того, что вы собираетесь использовать необязательный, который вы связываете.

Ответ 3

Я лично считаю, что это выглядит неприятно, потому что вы сравниваете нуль с вашим результатом, а не своим результатом, с нолем:

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

Поскольку вы только хотите убедиться, что это не нуль, а не использовать item, нет смысла использовать let.

Ответ 4

Ответ AirspeedVelocity показывает нам, что let _ = и != nil создают один и тот же код сборки, поэтому я настоятельно рекомендую использовать первый подход.

На самом деле, если у вас есть что-то вроде:

if let _ = optional {
    do_something()
}

... и вы хотите добавить какой-то код, и теперь вам нужно это необязательно, это изменение будет проще и быстрее:

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

Используйте let _ = и напишите код поддерживаемого кода.