2017-08-29 35 views
4

是否可以強制關閉完成?同樣,具有返回值的函數必須總是返回,如果有辦法強制關閉包含總是完成所需的語法,那麼這將是ace。快速關閉 - 強制關閉始終完成

例如,該代碼不會編譯因爲函數並不總是返回值:

func isTheEarthFlat(withUserIQ userIQ: Int) -> Bool { 
    if userIQ > 10 { 
     return false 
    } 
} 

在完全相同的方式,我想定義具有關閉功能,這如果閉包永不返回,也不會編譯。例如,下面的代碼可能永遠不會返回completionHandler:

func isTheEarthFlat(withUserIQ userIQ: Int, completionHandler: (Bool) -> Void) { 
    if userIQ > 10 { 
     completionHandler(false) 
    } 
} 

上面的代碼編譯,但我不知道是否有是強制執行關閉在所有情況下發送完成處理程序的關鍵字。可能它與上述函數中的Void有關?

+1

不是一種語言的水平,雖然我的怎麼可能會達到一個有趣的想法。讓我嘗試一些東西 – Alexander

+0

難道你不能只是在方法的最後調用完成處理程序嗎? – Sweeper

+0

@Sweeper我們的大多數完成處理程序都用於服務器訪問,所以它們將返回(1)發生的任何錯誤和(2)來自服務器的數據。由於服務器錯誤不是我們想向用戶顯示的同一錯誤,因此我們通常會有很多功能來決定錯誤是什麼,同樣我們也可能希望以特定方式操作從服務器返回的數據。我要求編譯器確保在代碼的所有分支中調用完成處理程序 - 與正常函數中返回值必須返回的方式完全相同。 – theDuncs

回答

2

不,如果您忘記(或不需要)在所有可能的條件下調用完成處理程序(如return語句),則不存在會導致編譯器錯誤的語言構造。

這是一個有趣的想法,可能會使語言有用的增強。也許在參數聲明中的某個地方使用required關鍵字。

+0

我一直在喝koolaid:https://stackoverflow.com/a/45945054/3141234 – Alexander

2

沒有什麼特別的關鍵字是你想要的。但你可以考慮一個有趣的方法,不會編譯:

func isTheEarthFlat(withUserIQ userIQ: Int, completionHandler: (Bool) -> Void) { 
    let result: Bool 
    defer { 
     completionHandler(result) 
    } 
    if userIQ > 10 { 
     result = false 
    } 
} 

會做,是completionHandler被迫叫:

func isTheEarthFlat(withUserIQ userIQ: Int, completionHandler: (Bool) -> Void) { 
    let result: Bool 
    defer { 
     completionHandler(result) 
    } 
    if userIQ > 10 { 
     result = false 
    } else { 
     result = true 
    } 
} 

不知道這是一個很好的模式使用。

2

這是我想到的一個有趣的技術。您定義了GuarenteedExecutionGuarenteedExecutionResult類型。

A GuarenteedExecution是一個閉包的包裝,它將在必須保證閉包執行的上下文中使用。

GuarenteedExecutionResult是執行GuarenteedExecution的結果。訣竅是具有所需的功能,例如isTheEarthFlat,返回GuarenteedExecutionResult。獲得GuarenteedExecutionResult實例的唯一方法是在GuarenteedExecution上調用execute(argument:)。實際上,類型檢查功能負責保證返回,現在正用於保證執行GuarenteedExecution

struct GuarenteedExecutionResult<R> { 
    let result: R 

    fileprivate init(result: R) { self.result = result } 
} 

struct GuarenteedExecution<A, R> { 
    typealias Closure = (A) -> R 

    let closure: Closure 

    init(ofClosure closure: @escaping Closure) { 
     self.closure = closure 
    } 

    func execute(argument: A) -> GuarenteedExecutionResult<R> { 
     let result = closure(argument) 
     return GuarenteedExecutionResult(result: result) 
    } 
} 

用法示例,在單獨文件(從而無法獲得GuarenteedExecutionResult.init):

let guarenteedExecutionClosure = GuarenteedExecution(ofClosure: { 
    print("This must be called!") 
}) 

func doSomething(guarenteedCallback: GuarenteedExecution<(),()>) 
    -> GuarenteedExecutionResult<()> { 
    print("Did something") 
    return guarenteedCallback.execute(argument:()) 
} 

_ = doSomething(guarenteedCallback: guarenteedExecutionClosure) 
+0

有趣的想法,與此唯一的問題是你將不得不爲每個參數定義一個不同的'GuaranteedExecution'你想要的。例如如果閉包的類型是'(A,B) - > R','(A,B,C) - > R'等,那麼該怎麼辦? – Paolo

+0

@Paolo是的,但是你可以用一個元組來代替。 A((X,Y,Z))→R)'匹配'(A) - > R',其中A是'(X,Y,Z) – Alexander