假设我有一个 Swift 类,它存储一个完成块,并执行一些异步任务。
我希望该块由首先完成的任务中的任何一个调用,但只有那个 - 我不希望在第二个任务完成时再次调用它。
我怎样才能以干净的方式实现这一点?
假设我有一个 Swift 类,它存储一个完成块,并执行一些异步任务。
我希望该块由首先完成的任务中的任何一个调用,但只有那个 - 我不希望在第二个任务完成时再次调用它。
我怎样才能以干净的方式实现这一点?
曾经有一个标准的表达方式。不幸的是,标准的 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
属性),则在整个程序的生命周期中只能执行一次完成。
只要你不需要它是线程安全的,你可以用一个相当简单的@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
}
用标记完成块var
,@ReadableOnce
它会在第一次读取它的值后被销毁。
像这样的东西:
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,即使您不调用闭包!对于不熟悉您编写的聪明的属性包装器的人来说,这可能会让他们感到惊讶。