0

我目前正在 Xcode 10 Playground (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”都被打印出来了。

如果我摆脱线程,它可以正常工作,但它似乎DispatchQueue.main.async在第一个有机会返回之前到达第二个。

在我的实际代码中的“一些缓慢的东西”中还有很多东西在发生,if但我不能依赖在调用第二次返回之前花费足够长的时间来返回。

我该如何做到这一点:让函数在后台线程中运行,但在主线程上只返回一次(就像通常没有线程的代码一样)?

4

3 回答 3

5

我相信你的目标是只调用completion一次处理程序,当你这样做时,你就完成了。在这种情况下,return.background主线程上将完成调用排队后调用线程:

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)
        }
    }
}
于 2019-07-23T11:07:42.830 回答
0

为什么它被称为两次很明显。(顺便说一句,你不返回完成块)你写的是这样的:

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等待所有请求完成

于 2019-07-23T10:38:01.983 回答
0

您可以使用defer语句在所有语句之后返回完成块一次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
        }
    }
于 2019-07-23T11:45:00.507 回答