也许我不了解完成处理程序的概念,但是我有一个类似的功能;
func someFunction(completion: @escaping (Bool, LoginError?) -> Void) {
self.checkDevice() { allowed, error in
if let e = error {
completion(false, e)
}
completion(true, nil)
}
}
了解checkDevice()
的功能时,基本前提是它执行异步网络调用,并返回没有错误(nil)的 true 或返回错误并显示错误。
运行此代码时,我发现完成处理程序被调用了两次。它以元组(false, error
)和(true, nil
)的形式发送完成。我已经进行了一些调试,似乎没有办法两次调用someFunction()
。
我相信,一旦发送完成,该功能就会结束。在我的测试案例中,我强制执行checkDevice()
中的错误,这应该导致我将完成发送为(false, error
,但是我同时看到了{false, error
)和({{ 1}})。完成不会立即结束功能吗?
答案 0 :(得分:3)
我相信一旦发送完成,功能就会结束。
不,为什么会这样?当您调用此函数时,就像调用任何其他函数一样。 名称没有魔力。
像这样重写它:
func someFunction(completion: @escaping (Bool, LoginError?) -> Void) {
self.checkDevice() { allowed, error in
if let e = error {
completion(false, e)
return // *
}
completion(true, nil)
}
}
实际上,我应该提到,这是编写完成处理程序的糟糕方法。与其采用两个参数(布尔型和一个可选的LoginError)(仅当布尔型为false
时才使用),它应该采用一个 one 参数-一个Result,它包含我们成功还是失败并且,如果失败了,错误是什么:
func someFunction(completion: @escaping (Result<Void, Error>) -> Void) {
self.checkDevice() { allowed, error in
completion( Result {
if let e = error { throw e }
})
}
}
如您所见,使用Result作为参数可以使您对发生的事情更加优雅。
实际上,checkDevice
本身可以将Result传递给其完成处理程序,这样事情就会变得更加优雅(更简单)。
答案 1 :(得分:1)
完成处理程序不会结束函数。结束函数的典型方法是插入 func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.row == 0{
return 0
}
return tableView.rowHeight
}
。
查看评论重新:您的具体情况
答案 2 :(得分:1)
考虑包括参数名称(供参考)。完成只能调用一次。您可以使用return
或使用完整的if-else
条件来做到这一点。
func someFunction(completion: @escaping (_ done: Bool, _ error: LoginError?) -> Void) {
checkDevice() { (allowed, error) in
if let e = error {
completion(false, e)
} else {
completion(true, nil)
}
}
}
func someFunction(completion: @escaping (_ done: Bool, _ error: LoginError?) -> Void) {
checkDevice() { (allowed, error) in
if let e = error {
completion(false, e)
return
}
completion(true, nil)
}
}
通过提供参数名称,在调用函数时,您现在可以引用签名来表示其参数的含义:
someFunction { (done, error) in
if let error = error {
...
} else if done {
...
}
}