web-dev-qa-db-ja.com

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

タイプAction?completionパラメーター(およびaction)を作成し、さらに@escapingを保持する方法はありますか?

タイプを変更すると、以下のエラーが発生します。

error: @escaping attribute only applies to function types

@escaping属性を削除すると、コードはコンパイルされて実行されますが、completionクロージャーが関数の範囲をエスケープしているため、正しくないようです。

140
Lescai Ionel

@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)->()>の同義語であるため、オプションのパラメータは暗黙的にエスケープされます。オプションのクロージャはエスケープします。

100
Jans

から: Swift-usersメーリングリスト

基本的に、@ escapingは関数パラメータ位置のクロージャに対してのみ有効です。 noescape-by-defaultルールはこれらのクロージャの関数パラメータ位置にのみ適用され、それ以外の場合はエスケープされます。関連付けられた値を持つ列挙型(例:Optional)、タプル、構造体などの集合体にクロージャがある場合は、関数パラメータの位置にないクロージャのデフォルトの規則に従います(つまりエスケープします)。

そのため、オプションの関数パラメータはデフォルトで@escapingです。
@ noeascapeはデフォルトで関数パラメータにのみ適用されます。

182
Dmitry Coolerov

私は似たような問題に遭遇します、そして@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)
}
15
Freeman Man

私はそれをSwift 3で動かしました。

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

この例で理解しておくべき重要なことは、ActionAction?に変更すると、クロージャーエスケープされることです。それで、あなたが提案することをしましょう:

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

まあ、その要件はクロージャを回避するためにのみ発生します。だから閉鎖は脱出している。だからエスケープしないとマークしない - すでにエスケープしているのです.

0
matt