Швидкий вибір необов'язкового параметра закриття


162

Подано:

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()

Чи є спосіб зробити completionпараметр (і action) типу, Action?а також зберегти @escaping?

Зміна типу видає таку помилку:

Атрибут @escaping стосується лише типів функцій

Видаляючи @escapingатрибут, код компілюється та запускається, але, здається, не є правильним, оскільки completionзакриття виходить із сфери функції.


21
«Видалення @escapingатрибута, код компілюється і працює» - це тому , що, як описано в SR-2444 , Action?є, за замовчуванням, тікаючи. Отже, видалення @escapingпід час використання додаткового закриття здійснює те, що вам потрібно.
Роб


Закриття типу псевдоніму втече
Masih

Ось чудова стаття Оле Бегемана, яка описує, чому це відбувається, і деякі обхідні шляхи, якщо ви хочете, щоб необов'язкові параметри були @noescape.
Розсудливий

Відповіді:


122

Існує звіт SR-2552, який @escapingне розпізнає псевдоніми функцій. тому помилка @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()

EDIT 1 :

Я фактично знаходився під бета-версією xcode 8, де помилка SR-2552 ще не була вирішена. виправивши помилку, представив новий (той, з ким ви стикаєтесь), який все ще відкритий. див. SR-2444 .

Обхід @Michael Ilseman вказав як тимчасове рішення - це видалити @escapingатрибут з необов'язкового типу функції, який зберігає функцію як біг .

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

EDIT 2 :

SR-2444 було закрито , конкретно вказавши , що закриття позицій в параметрах НЕ уникнути , і потрібно , щоб вони були помічені , @escapingщоб їх уникнути, але необов'язкові параметри будуть неявно уникнути, так як ((Int)->())?є синонімами Optional<(Int)->()>, факультативні закриття тікають.


5
Зараз отримуємо@escaping may only be applied to parameters of function type func doStuff(stuff: String, completion: (@escaping ()->())?) {
Лешкай Іонель

1
a temporary solution is remove the @escaping attribute from optional function type, that keep the function as escaping. Чи можете ви пояснити це далі? Семантичне значення за замовчуванням у swift 3 не ухиляється. Хоча він компілюється без @escaping, я боюся, що це спричинить проблеми, якщо трактуватись як не втечу. Це неправда?
Пат Німейер

49
Після подальшого читання я бачу, що SR-2444 каже, що всі необов'язкові закриття трактуються як втеча, що є додатковою помилкою :) Я припускаю, що коли вона буде виправлена, компіляція попередить нас внести зміни.
Пат Німейер

Можливо, трохи поза темою; але як це працює @autoclosure? Один отримує ту саму помилку ...
Gee.E

це працює без @escaping __ func doStuff (матеріал: Рядок, завершення: (() -> ())?
Феннур Мезитов

226

від: список розсилки швидких користувачів

В основному, @escaping дійсний лише при закриттях у положенні функціонального параметра. Правило noescape за замовчуванням застосовується лише до цих закриттів у положенні функціонального параметра, інакше вони проходять. Агрегати, такі як переписки з пов'язаними значеннями (наприклад, необов'язково), кортежі, структури тощо, якщо вони мають закриття, дотримуйтесь правил за замовчуванням для закриттів, які не перебувають у функціональному параметрі, тобто вони виходять.

Тож додатковим параметром функції за замовчуванням є @escaping.
@noeascape застосовується лише до параметру функції за замовчуванням.


7
Я думаю, що це додає найважливішу інформацію до теми, на це слід прийняти відповідь.
Даміан Дудич

Обґрунтована відповідь. Наскільки ймовірно, що це може змінитися?
GoldenJoe

2
Це має сенс, оскільки технічно сказати (()->Void)?- це те саме, що сказати, що у вас є, Optional<()->Void>і для того, Optionalщоб зберегти право власності, йому доведеться лише приймати @escapingфункції. Третє, що це має бути прийнята відповідь. Дякую.
Дін Келлі

22

Я зіткнувся з подібною проблемою, тому що змішування @escapingі не @escapingдуже заплутано, особливо якщо вам потрібно пройти закриття навколо.

Я в кінцевому підсумку призначив параметр закриття за замовчуванням через опцію = { _ in }, що, на мою думку, має більше сенсу:

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)
}

Це чисто і досить в реалізації.
Фред Фауст

2
Що робити, якщо я хочу перевірити, чи цей блок нуль / порожній?
В’ячаслав Герчич

2
Баммер, це не працює для протокольного методу. "Аргумент за замовчуванням не дозволений у методі протоколу" (Xcode 8.3.2).
Майк Таверн

17

У мене це працює в Swift 3 без будь-яких попереджень:

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

4

Важливо зрозуміти в прикладі, що якщо ви переходите 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
        }
    }
}

Ну, ця вимога виникає лише для уникнення закриття. Тож закриття втече. Ось чому ви не відзначаєте це втечею - це вже втеча.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.