簡體   English   中英

Swift 可選的轉義閉包參數

[英]Swift optional escaping closure parameter

鑒於:

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參數(和actionAction? 並保持@escaping

更改類型會出現以下錯誤:

@escaping 屬性僅適用於函數類型

刪除@escaping屬性,代碼編譯並運行,但似乎不正確,因為completion閉包正在轉義函數的范圍。

來自: swift-users 郵件列表

基本上,@escaping 僅對函數參數位置的閉包有效。 noescape-by-default 規則僅適用於函數參數位置的這些閉包,否則它們將被轉義。 聚合體,例如帶有關聯值的枚舉(例如 Optional)、元組、結構體等,如果它們有閉包,那么對於不在函數參數位置的閉包,即它們是轉義的,遵循默認規則。

所以可選的函數參數默認是@escaping。
@noeascape 默認僅適用於函數參數。

有一個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()

編輯 1: :

我實際上是在 xcode 8 beta 版本下,其中錯誤SR-2552尚未解決。 修復該錯誤,引入了一個仍處於打開狀態的新錯誤(您面臨的錯誤)。 SR-2444

@Michael Ilseman指出的臨時解決方法是從可選函數類型中刪除@escaping屬性,使函數保持為 escaping

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

編輯2 ::

SR-2444已經被關閉,說明明確,在參數關閉位置逃逸,需要他們被打上@escaping讓他們逃脫,但可選參數隱含逃脫,因為((Int)->())? Optional<(Int)->()>的同義詞,可選閉包正在轉義。

我遇到了類似的問題,因為混合@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)
}

我讓它在 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
        }
    }
}

嗯,這個要求只出現在轉義閉包中。 所以關閉正在逃逸。 這就是為什么你沒有標記為轉義 - 它已經在轉義了。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM