Быстрое закрытие по выбору

введите описание изображения здесь

Ошибка компилятора Closure use of non-escaping parameter 'completion' may allow it to escape, что имеет смысл, потому что она будет вызвана после возврата функции.

func sync(completion:(()->())) {
    self.remoteConfig.fetch(withExpirationDuration: TimeInterval(expirationDuration)) { (status, error) -> Void in
        completion()
    }
}

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

func sync(completion:(()->())?) {
    self.remoteConfig.fetch(withExpirationDuration: TimeInterval(expirationDuration)) { (status, error) -> Void in
        completion?()
    }
}

person Bilal    schedule 21.11.2017    source источник
comment
Возможный дубликат stackoverflow.com/q/39618803/2976878 (в частности, этот ответ: stackoverflow.com/a/39846519/2976878)   -  person Hamish    schedule 21.11.2017
comment
Возможно, не совсем дубликат, но большое спасибо за ссылки @Hamish   -  person matt    schedule 21.11.2017
comment
Вот отличная статья Оле Бегеманна, в которой объясняется, почему это происходит и некоторые обходные пути, если вы хотите, чтобы необязательные параметры были @noescape.   -  person Senseful    schedule 31.05.2018


Ответы (2)


Уточнение:

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

typealias completion = () -> ()

enum CompletionHandler {
    case success
    case failure

    static var handler: completion {
        get { return { } }
        set { }
    }
}

func doSomething(handlerParameter: completion) {
    let chObject = CompletionHandler.handler = handlerParameter
}

На первый взгляд этот код кажется законным, но это не так! вы получите сообщение об ошибке времени компиляции:

ошибка: присвоение неоткрывающего параметра handlerParameter закрытию @escaping

let chObject = CompletionHandler.handler = handlerParameter

с примечанием, что:

примечание: параметр 'handlerParameter' неявно не экранирующий func doSomething (handlerParameter: Завершение) {

Это почему? предполагается, что фрагмент кода не имеет ничего общего с _2 _...

Фактически, поскольку был выпущен Swift 3, замыкание будет «экранировано», если оно объявлено в enum, struct или class по умолчанию.

Для справки сообщаем об ошибках, связанных с этой проблемой:

Хотя они могут не на 100% относиться к этому делу, комментарии правопреемника четко описывают дело:

Первый комментарий:

Фактическая проблема здесь в том, что необязательные закрытия прямо сейчас неявно @escaping.

Второй комментарий:

К сожалению, это относится к Swift 3. Вот семантика экранирования в Swift 3:

1) Замыкания в позиции параметра функции по умолчанию не экранируются.

2) Все остальные закрытия ускользают

Таким образом, все замыкания аргументов универсального типа, такие как Array и Optional, экранируются.

Очевидно, Optional - это перечисление.

Также, как упоминалось выше, такое же поведение применимо к классам и структурам:

Дело класса:

typealias completion = () -> ()

class CompletionHandler {
    var handler: () -> ()

    init(handler: () -> ()) {
        self.handler = handler
    }
}

func doSomething(handlerParameter: completion) {
    let chObject = CompletionHandler(handler: handlerParameter)
}

Случай структуры:

typealias completion = () -> ()

struct CompletionHandler {
    var handler: completion
}

func doSomething(handlerParameter: completion) {
    let chObject = CompletionHandler(handler: handlerParameter)
}

Два приведенных выше фрагмента кода приведут к одному и тому же результату (ошибка времени компиляции).

Для исправления ситуации вам необходимо разрешить подпись функции:

func doSomething( handlerParameter: @escaping completion)


Вернемся к основному вопросу:

Поскольку вы ожидаете, что вы должны позволить completion:(()->())? быть экранированным, это будет сделано автоматически - как описано выше-.

person Ahmad F    schedule 21.11.2017

Помещение замыкания в Optional автоматически отмечает его экранирование. Технически он уже «ускользнул», будучи встроенным в перечисление (Необязательно).

person Rob Napier    schedule 21.11.2017
comment
почему это так? В чем идея? - person TNguyen; 21.11.2017
comment
Очаровательный. Я полагаю, это потому, что, будучи назначенным как значение свойства внутри оболочки, оно уже было экранировано. - person matt; 21.11.2017
comment
@matt Совершенно верно. Если вы вставите замыкание внутри структуры, оно также будет автоматически экранировано. - person Rob Napier; 21.11.2017
comment
@matt да, я понимаю, как работает необязательный, но для меня это было неочевидно. Очень интересно! - person TNguyen; 21.11.2017
comment
Им следует рассмотреть возможность использования первого предложения в качестве сообщения об ошибке. - person somebody4; 16.08.2019