[英]How do you write a Swift completion block that can only be called once?
假設我有一個存儲完成塊的 Swift 類,並執行一些異步任務。
我希望該塊由首先完成的任務調用,但只有那個 - 我不希望在第二個任務完成時再次調用它。
我怎樣才能以一種干凈的方式實現這一點?
只要你不需要它是線程安全的,你就可以用一個相當簡單的@propertyWrapper
來解決這個問題。
@propertyWrapper
struct ReadableOnce<T> {
var wrappedValue: T? {
mutating get {
defer { self._value = nil }
return self._value
}
set {
self._value = newValue
}
}
private var _value: T? = nil
}
用@ReadableOnce
標記完成塊var
,它會在第一次讀取它的值后銷毀。
像這樣的東西:
class MyClass {
@ReadableOnce private var completion: ((Error?) -> Void)?
init(completion: @escaping ((Error?) -> Void)) {
self.completion = completion
}
public func doSomething() {
// These could all be invoked from different places, like your separate tasks' asynchronous callbacks
self.completion?(error) // This triggers the callback, then the property wrapper sets it to nil.
self.completion?(error) // This does nothing
self.completion?(error) // This does nothing
}
}
我在這里寫了更詳細的討論,但要注意的關鍵是讀取值會將其設置為 nil,即使您不調用閉包! 對於不熟悉您編寫的智能屬性包裝器的人來說,這可能會令人驚訝。
已經有一個標准的一次性表達方式。 不幸的是,標准的 Objective-C 在 Swift 中不可用(GCD dispatch_once
),但標准的 Swift 技術可以正常工作,即具有惰性定義和調用初始化程序的屬性。
具體如何執行此操作取決於您希望強制執行一次性的級別。 在此示例中,它位於類實例級別:
class MyClass {
// private part
private let completion : (() -> ())
private lazy var once : Void = {
self.completion()
}()
private func doCompletionOnce() {
_ = self.once
}
// public-facing part
init(completion:@escaping () -> ()) {
self.completion = completion
}
func doCompletion() {
self.doCompletionOnce()
}
}
在這里我們將測試它:
let c = MyClass() {
print("howdy")
}
c.doCompletion() // howdy
c.doCompletion()
let cc = MyClass() {
print("howdy2")
}
cc.doCompletion() // howdy2
cc.doCompletion()
如果將私有內容提升到類級別(使用靜態once
屬性),則在整個程序的生命周期中只能執行一次完成。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.