Обновление закрытий для Swift 3 - @escaping

Я обновил свой код до Xcode 8.0 beta 6, но я застрял в том, что, похоже, связано с невыполнением нового закрывающего закрытия. В следующем коде Xcode предлагает добавить @escaping перед completion: в первой строке приведенного ниже кода, но это все равно не будет компилироваться и перемещаться по кругу. *

( ИЗМЕНИТЬ: на самом деле, @escaping следует добавить в после completion:, как предлагает Xcode. Предупреждение может показываться, но очистка и компиляция удаляют его.) * Как этот код должен быть переписан/исправлен для работы в обновленном Swift 3? Я просмотрел новое руководство, но не смог найти правильные образцы кода.

func doSomething(withParameter parameter: Int, completion: () -> ()) {
    // Does something

    callSomeOtherFunc(withCompletion: completion)
  }

// Calling the method and execute closure 
doSomething(withParameter: 2) {
  // do things in closure
}

Любая помощь очень ценится!

Ответ 1

Swift 3: атрибуты параметра закрытия теперь применяются к типу параметра, а не самому параметру

До Swift 3 атрибуты закрытия @autoclosure и @noescape были атрибутами для параметра закрытия, но теперь являются атрибутами типа параметра; см. следующее принятое предложение о развитии Swift:

Ваш конкретный вопрос относится к атрибуту type @escaping (для которого применяется то же новое правило), как описано в принятом предложении Swift для того, чтобы параметры закрытия не были экранированы по умолчанию:

Эти предложения теперь реализованы на стадии бета-тестирования Xcode 8 (см. примечания к выпуску для Xcode 8 beta 6; для входа требуется учетная запись для входа в систему)

Новое в Xcode 8 beta 6 - Swift-компилятор: Swift Language

Параметры закрывания по умолчанию не экранируются, а не явно аннотируется с помощью @noescape. Используйте @escaping, чтобы указать, что параметр закрытия может исчезнуть. @autoclosure(escaping) теперь записывается как @autoclosure @escaping. Аннотации @noescape и @autoclosure(escaping) устарели. (SE-0103)

...

Новое в Xcode 8 beta - Swift и Apple LLVM Compilers: Swift Language

Теперь атрибуты @noescape и @autoclosure должны быть записаны перед типом параметра вместо имени параметра. [SE-0049]

Следовательно, вы используете атрибут @escaping не по умолчанию; применяется к типу параметра закрытия, а не самому параметру

func doSomething(withParameter parameter: Int, completion: @escaping () -> ()) {
    // ...
}

(Включая мой ответ на вопрос в приведенном ниже комментарии, поскольку комментарии не являются постоянными данными о SO)

@Cristi Băluţă: "Что делает побег? Никогда не видел эти ключевые слова перед автоматическим преобразованием swift3..."

См. ссылка на предложение SE-0103 по эволюции выше (а также цитированный текст из бета-версий бета-версии): ранее параметры закрытия были экранированы по умолчанию (следовательно, нет необходимость существования явной аннотации для экранирования), но теперь вместо этого не выполняется экранирование по умолчанию. Следовательно, добавление @escaping позволяет явно аннотировать, что параметр закрытия может уйти (вопреки его поведению по умолчанию). Это также объясняет, почему @noescape теперь устарел (нет необходимости комментировать поведение по умолчанию).

Для объяснения того, что означает, что параметр закрытия экранируется, я цитирую Reference Reference - атрибуты:

"Применить этот атрибут к типу параметров в объявлении метода или функции, чтобы указать, что значение параметров может быть сохранено для позднее исполнение. Это означает, что значение позволяет переживать время жизни вызова.

Ответ 2

@noescape

От xcode 8 beta 6 @noescape по умолчанию. До этого @escaping был значением по умолчанию. Любое обновление до версии 3.0 из предыдущих версий может столкнуться с этой ошибкой.

Вы не можете сохранить закрытие @noescape внутри переменной. Потому что, если вы можете сохранить закрытие внутри переменной, вы можете выполнить закрытие из любого места вашего кода. Но @noescape утверждает, что параметр закрытия не может выйти из тела функции.

Это даст ошибку компилятора в Xcode 8

class MyClass {

    var myClosure: (() -> ())?

    func doSomething(finishBlock: () -> ()) {
        myClosure = finishBlock    // ‼️ Error: Assigning non-escaping parameter 'finishBlock' to an @escaping closure
    }
}

Это скомпилирует ok (явно пишут @escaping)

class MyClass {

    var myClosure: (() -> ())?

    func doSomething(finishBlock: @escaping () -> ()) {
        myClosure = finishBlock
    }
}

Преимущества @noescape:

  • Компилятор может оптимизировать ваш код для повышения производительности
  • Компилятор может позаботиться об управлении памятью
  • Нет необходимости использовать слабую ссылку на self в закрытии


Для получения дополнительной информации ознакомьтесь с: Сделать неэксклюзивное закрытие по умолчанию