Опциональный параметр закрытия с быстрой скоростью 3

Дано:

typealias Action = () -> ()

var action: Action = { }

func doStuff(stuff: String, completion: @escaping Action) {
    print(stuff)
    action = completion
    completion()
}

func doStuffAgain() {
    print("again")
    action()
}

doStuff(stuff: "do stuff") { 
    print("swift 3!")
}

doStuffAgain()

Есть ли способ сделать параметр completionaction) типа Action?, а также сохранить @escaping?

Изменение типа дает следующую ошибку:

error: @escaping attribute only applies to function types

Удаление атрибута @escaping, код компилируется и запускается, но не кажется правильным, так как закрытие completion ускоряет область действия функции.

Ответ 1

SR-2552 сообщает, что @escaping не распознает псевдоним типа функции. поэтому @escaping attribute only applies to function types error @escaping attribute only applies to function types. Вы можете обойти это, расширив тип функции в сигнатуре функции:

typealias Action = () -> ()

var action: Action? = { }

func doStuff(stuff: String, completion: (@escaping ()->())?) {
    print(stuff)
    action = completion
    completion?()
}

func doStuffAgain() {
    print("again")
    action?()
}

doStuff(stuff: "do stuff") {
    print("swift 3!")
}

doStuffAgain()

РЕДАКТИРОВАТЬ 1:

Я был на самом деле под бета-версии xcode 8, где ошибка SR-2552 еще не была решена. исправляя эту ошибку, представил новую (ту, с которой вы столкнулись), которая все еще открыта. см. SR-2444.

Обходной путь @Michael Ilseman указал, что временным решением является удаление атрибута @escaping из необязательного типа функции, который сохраняет функцию как экранирующую.

func doStuff(stuff: String, completion: Action?) {...}

РЕДАКТИРОВАТЬ 2:

SR-2444 был закрыт, конкретно указав, что закрытие в параметрах позиции не избежать, и нужно, чтобы они были отмечены @escaping, чтобы сделать их побега, но необязательные параметры неявно избежать, так как ((Int)->())? является синонимом Optional<(Int)->()>, дополнительные замыкания экранируют.

Ответ 2

from: список рассылки быстрых пользователей  

В принципе, @escaping действителен только при замыканиях в позиции параметров функции. Правило noescape-by-default применяется только к этим замыканиям в позиции параметров функции, в противном случае они экранируются. Агрегаты, такие как перечисления со связанными значениями (например, необязательные), кортежи, структуры и т.д., Если они имеют замыкания, следуют правилам по умолчанию для закрытий, которые не находятся в позиции параметров функции, то есть они ускользают.

Таким образом, необязательным параметром функции является @escaping по умолчанию.
@noeascape по умолчанию применяется только к параметру функции.

Ответ 3

Я сталкиваюсь с аналогичной проблемой, и потому что смешивание @escaping и non @escaping очень сбивает с толку, особенно если вам нужно пройти закрытие. Я в конечном итоге с параметрами по умолчанию (что, я думаю, имеет больше смысла)

func doStuff(stuff: String = "do stuff",
        completion: @escaping (_ some: String) -> Void = { _ in }) {
     completion(stuff)
}

doStuff(stuff: "bla") {
    stuff in
    print(stuff)
}

doStuff() {
    stuff in
    print(stuff)
}

Ответ 4

Я получил его в Swift 3 без каких-либо предупреждений только таким образом:

func doStuff(stuff: String, completion: (()->())? ) {
    print(stuff)
    action = completion
    completion?()
}

Ответ 5

В этом примере важно понять, что если вы измените Action на Action? закрытие сбегает. Итак, давайте делать то, что вы предлагаете:

typealias Action = () -> ()

var action: Action? = { }

func doStuff(stuff: String, completion: Action?) {
    print(stuff)
    action = completion
    completion?()
}

Хорошо, теперь мы будем называть doStuff:

class ViewController: UIViewController {
    var prop = ""
    override func viewDidLoad() {
        super.viewDidLoad()
        doStuff(stuff: "do stuff") {
            print("swift 3!")
            print(prop) // error: Reference to property 'prop' in closure 
                        // requires explicit 'self.' to make capture semantics explicit
        }
    }
}

Ну, это требование возникает только для избежания замыканий. Так что закрытие ускользает. Вот почему вы не помечаете его как побег - он уже сбежал.