Опциональный параметр закрытия с быстрой скоростью 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()
Есть ли способ сделать параметр completion
(и action
) типа 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
}
}
}
Ну, это требование возникает только для избежания замыканий. Так что закрытие ускользает. Вот почему вы не помечаете его как побег - он уже сбежал.