完成处理程序调用两次(带线程)
Completion handler called twice (with threading)
我目前正在 Xcode 10 游乐场 (Swift 5) 中测试此代码:
func one() {
let test = "bla"
two(test, completion: { (returned) in
print(returned)
})
}
func two(_ test: String, completion: @escaping (_ returned: String?) -> Void) {
DispatchQueue.global(qos:.background).async {
if !test.isEmpty {
//Some slow stuff
DispatchQueue.main.async {
return completion("hi!")
}
}
//Also some slow stuff
DispatchQueue.main.async {
return completion(nil) //can't have this in "else"!
}
}
}
one()
问题是 "hi" 和 "nil" 都被打印了。
如果我摆脱线程,它工作正常但它似乎在第一个有机会 return.
之前到达第二个 DispatchQueue.main.async
在我的实际代码中 "Some slow stuff" if
中发生了很多事情,但我不能指望在第二个 [= 之前 return 花费足够长的时间27=] 也被调用。
我该如何实现:在后台线程中使用函数 运行,但在主线程中仅使用一次 return(就像没有线程的代码通常那样)?
为什么它被调用两次很明显。 (顺便说一句,你没有 return 完成块)你写的是这样的:
func two(_ test: String, completion: @escaping (_ returned: String?) -> Void) {
DispatchQueue.global(qos:.background).async {
if !test.isEmpty {
//Some slow stuff
DispatchQueue.main.async {
completion("hi!")
}
}
completion(nil) //can't have this in "else"!
}
}
并且由于主线程始终保持运行,而您在后台威胁上做其他事情,您将获得 2 次完成。我猜你想做的是删除第二个?
另一种方法是创建一个 DispatchGroup
并在其中输入每个调用,然后编写一个 Dispatch wait till done
以等待所有请求完成
我相信你的目标是只调用一次 completion
处理程序,当你这样做时你就完成了。在那种情况下,在主线程上排队完成调用后,在 .background
线程中调用 return
:
func two(_ test: String, completion: @escaping (_ returned: String?) -> Void) {
DispatchQueue.global(qos:.background).async {
if !test.isEmpty {
//Some slow stuff
// notify main thread we're done
DispatchQueue.main.async {
completion("hi!")
}
// we are done and don't want to do more work on the
// background thread
return
}
//Also some slow stuff
DispatchQueue.main.async {
completion(nil)
}
}
}
您可以使用defer
语句来return 完成所有If
语句之后的块。这里只是您的代码示例,但我希望它很清楚。
func two(_ test: String, completion: @escaping (_ returned: String?) -> Void) {
DispatchQueue.global(qos:.background).async {
var resultString: String?
// Called only once after all code inside this async block.
defer {
DispatchQueue.main.async {
completion(resultString)
}
}
if !test.isEmpty {
//Some slow stuff
resultString = "hi"
return
}
// Another stuff
resultString = nil
}
}
我目前正在 Xcode 10 游乐场 (Swift 5) 中测试此代码:
func one() {
let test = "bla"
two(test, completion: { (returned) in
print(returned)
})
}
func two(_ test: String, completion: @escaping (_ returned: String?) -> Void) {
DispatchQueue.global(qos:.background).async {
if !test.isEmpty {
//Some slow stuff
DispatchQueue.main.async {
return completion("hi!")
}
}
//Also some slow stuff
DispatchQueue.main.async {
return completion(nil) //can't have this in "else"!
}
}
}
one()
问题是 "hi" 和 "nil" 都被打印了。
如果我摆脱线程,它工作正常但它似乎在第一个有机会 return.
之前到达第二个DispatchQueue.main.async
在我的实际代码中 "Some slow stuff" if
中发生了很多事情,但我不能指望在第二个 [= 之前 return 花费足够长的时间27=] 也被调用。
我该如何实现:在后台线程中使用函数 运行,但在主线程中仅使用一次 return(就像没有线程的代码通常那样)?
为什么它被调用两次很明显。 (顺便说一句,你没有 return 完成块)你写的是这样的:
func two(_ test: String, completion: @escaping (_ returned: String?) -> Void) {
DispatchQueue.global(qos:.background).async {
if !test.isEmpty {
//Some slow stuff
DispatchQueue.main.async {
completion("hi!")
}
}
completion(nil) //can't have this in "else"!
}
}
并且由于主线程始终保持运行,而您在后台威胁上做其他事情,您将获得 2 次完成。我猜你想做的是删除第二个?
另一种方法是创建一个 DispatchGroup
并在其中输入每个调用,然后编写一个 Dispatch wait till done
以等待所有请求完成
我相信你的目标是只调用一次 completion
处理程序,当你这样做时你就完成了。在那种情况下,在主线程上排队完成调用后,在 .background
线程中调用 return
:
func two(_ test: String, completion: @escaping (_ returned: String?) -> Void) {
DispatchQueue.global(qos:.background).async {
if !test.isEmpty {
//Some slow stuff
// notify main thread we're done
DispatchQueue.main.async {
completion("hi!")
}
// we are done and don't want to do more work on the
// background thread
return
}
//Also some slow stuff
DispatchQueue.main.async {
completion(nil)
}
}
}
您可以使用defer
语句来return 完成所有If
语句之后的块。这里只是您的代码示例,但我希望它很清楚。
func two(_ test: String, completion: @escaping (_ returned: String?) -> Void) {
DispatchQueue.global(qos:.background).async {
var resultString: String?
// Called only once after all code inside this async block.
defer {
DispatchQueue.main.async {
completion(resultString)
}
}
if !test.isEmpty {
//Some slow stuff
resultString = "hi"
return
}
// Another stuff
resultString = nil
}
}