与えられた:
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()
タイプAction?
のcompletion
パラメーター(およびaction
)を作成し、さらに@escaping
を保持する方法はありますか?
タイプを変更すると、以下のエラーが発生します。
error: @escaping attribute only applies to function types
@escaping
属性を削除すると、コードはコンパイルされて実行されますが、completion
クロージャーが関数の範囲をエスケープしているため、正しくないようです。
@escaping
が関数型の別名を認識していないと報告する SR-2552 があります。それがエラー@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::
私は実際にはバグ SR-2552 がまだ解決されていないxcode 8ベータ版の下にいました。そのバグを修正し、まだ開いている新しいもの(あなたが直面しているもの)を紹介しました。 SR-2444 を参照してください。
一時的な解決策として指摘されている回避策@ Michael Ilsemanは、オプションの関数型から@escaping
属性を削除して、関数をエスケープするようにします.
func doStuff(stuff: String, completion: Action?) {...}
編集2::
SR-2444 は、パラメータ位置でのクロージャはエスケープではないで、@escaping
でマークする必要があることを明示的に述べてクローズされました。 ((Int)->())?
はOptional<(Int)->()>
の同義語であるため、オプションのパラメータは暗黙的にエスケープされます。オプションのクロージャはエスケープします。
基本的に、@ escapingは関数パラメータ位置のクロージャに対してのみ有効です。 noescape-by-defaultルールはこれらのクロージャの関数パラメータ位置にのみ適用され、それ以外の場合はエスケープされます。関連付けられた値を持つ列挙型(例:Optional)、タプル、構造体などの集合体にクロージャがある場合は、関数パラメータの位置にないクロージャのデフォルトの規則に従います(つまりエスケープします)。
そのため、オプションの関数パラメータはデフォルトで@escapingです。
@ noeascapeはデフォルトで関数パラメータにのみ適用されます。
私は似たような問題に遭遇します、そして@escapingと非@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)
}
私はそれをSwift 3で動かしました。
func doStuff(stuff: String, completion: (()->())? ) {
print(stuff)
action = completion
completion?()
}
この例で理解しておくべき重要なことは、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
}
}
}
まあ、その要件はクロージャを回避するためにのみ発生します。だから閉鎖は脱出している。だからエスケープしないとマークしない - すでにエスケープしているのです.