我喜欢延迟,因为在具有多案例情况的复杂函数中,我不能忘记在函数结束之前调用完成。
但是当完成传递给另一个函数时,不应在第一个函数的延迟中运行它。
这是我通常要做的:
func doSomething(completion: ()->()) {
/// Set this to false, before passing the completion to another function.
var runCompletionInDefer = true
defer {
if runCompletionInDefer { completion() }
}
runCompletionInDefer = false
doSomethingElse { completion() }
}
func doSomethingElse(completion: ()->()) {
completion()
}
有更好的解决方案吗?
使用这种方法,在将完成传递给另一个函数之前,您仍然必须记住要这样做:
runCompletionInDefer = false
是否有理由无法将您的defer块中的代码移入完成处理程序?
Defer很好,但是我们肯定需要使用defer来解决。
[我不认为您应该将代码放入defer
中,直到您确信每次到达函数末尾都确实要执行该代码。
所以让我建议,也许您实际想要的defer
并不是完成闭包的字面执行,而是一种检查,它使您有信心当时那个人已经在某个地方调用了闭包达到defer
。一种“执行收据”。
为此,可以将闭包包装到跟踪其执行的结构中。这是一个这样的包装看起来像的例子:
protocol CompletionReceiptProtocol {
associatedtype Closure: Any
var receipt: Bool { get }
func execute()
}
class SomethingCompletion: CompletionReceiptProtocol {
typealias Closure = (()->Void)
var receipt = false
private var closure: Closure
init(_ closure: @escaping Closure) {
self.closure = closure
}
func execute() {
self.closure()
self.receipt = true
}
}
这是使用包装器的方式:
func doSomething(completion nakedCompletion: @escaping ()->Void) {
let completion = SomethingCompletion(nakedCompletion)
defer {
assert(completion.receipt , "Oops! End of function reached without completion being called!")
}
// Scenario A: `doSomething` executes the completion itself
completion.execute()
// Scenario B: `doSomething` delegates the completion to `doSomethingElse`...
doSomethingElse(completion: completion)
}
func doSomethingElse(completion: SomethingCompletion) {
completion.execute() //... and it's called here instead. So it's all good!
}