ios 在 Swift 中等待异步操作完成

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/24725059/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 01:22:40  来源:igfitidea点击:

Wait For Asynchronous Operation To Complete in Swift

iosswiftasynchronous

提问by PretzelJesus

I am not sure how to handle this situation as I am very new to iOS development and Swift. I am performing data fetching like so:

我不确定如何处理这种情况,因为我对 iOS 开发和 Swift 非常陌生。我正在执行这样的数据获取:

func application(application: UIApplication!, performFetchWithCompletionHandler completionHandler: ((UIBackgroundFetchResult) -> Void)!)
{
    loadShows()
    completionHandler(UIBackgroundFetchResult.NewData)
    println("Background Fetch Complete")
}

My loadShows() function parses a bunch of data it gets from a website loaded into a UIWebView. The problem is that I have a timer that waits for 10 seconds or so in the loadShows function. This allows for the javascript in the page to fully load before I start parsing the data. My problem is that the completion handler completes before my loadShows() does.

我的 loadShows() 函数解析从加载到 UIWebView 的网站获取的一堆数据。问题是我有一个在 loadShows 函数中等待 10 秒左右的计时器。这允许页面中的 javascript 在我开始解析数据之前完全加载。我的问题是完成处理程序在我的 loadShows() 之前完成。

What I would like to do is add a bool for "isCompletedParsingShows" and make the completionHandler line wait to complete until that bool is true. What is the best way to handle this?

我想要做的是为“isCompletedParsingShows”添加一个 bool 并让 completionHandler 行等待完成,直到该 bool 为真。处理这个问题的最佳方法是什么?

回答by Daij-Djan

you have to pass your async function the handler to call later on:

您必须将异步函数传递给稍后调用的处理程序:

func application(application: UIApplication!, performFetchWithCompletionHandler completionHandler: ((UIBackgroundFetchResult) -> Void)!) {
    loadShows(completionHandler)
}

func loadShows(completionHandler: ((UIBackgroundFetchResult) -> Void)!) {
    //....
    //DO IT
    //....

    completionHandler(UIBackgroundFetchResult.NewData)
    println("Background Fetch Complete")
}

OR (cleaner way IMHO)

或(恕我直言,更清洁的方式)

add an intermediate completionHandler

添加一个中间的completionHandler

func application(application: UIApplication!, performFetchWithCompletionHandler completionHandler: ((UIBackgroundFetchResult) -> Void)!) {
    loadShows() {
        completionHandler(UIBackgroundFetchResult.NewData)
        println("Background Fetch Complete")
    }
}

func loadShows(completionHandler: (() -> Void)!) {
    //....
    //DO IT
    //....
    completionHandler()
}

回答by Alex

two ways to solve this, both use Grand Central Dispatch(which is similar in Swift and Objective C):

解决这个问题的两种方法,都使用Grand Central Dispatch(在 Swift 和 Objective C 中类似):

  1. change loadShowsmethod to make it synchronous and use the same dispatch queue as completionHandler, then wrap the entire body of the method in a dispatch_async; this way the method call ends right away, but the completionHandler will be called after loadShows if finished, just like in a synchronous program

  2. use a GCD semaphore - just like the BOOL you mention, but created with dispatch_semaphore_create; you call dispatch_semaphore_waitbefore completionHandlerto make it wait for the semaphore to be unlocked (unlock it with dispatch_semaphore_signal) ; remember to place your method body inside a dispatch_asynccall in order not to have it block the rest of the app while waiting for loadShows to complete.

  1. 更改loadShows方法使其同步并使用与completionHandler相同的调度队列,然后将该方法的整个主体包装在dispatch_async 中;这样方法调用就结束了,但是如果完成了,会在loadShows之后调用completionHandler,就像在同步程序中一样

  2. 使用 GCD 信号量 - 就像您提到的 BOOL 一样,但使用dispatch_semaphore_create创建;您在completionHandler之前调用dispatch_semaphore_wait以使其等待信号量被解锁(使用dispatch_semaphore_signal解锁);请记住将您的方法主体放在dispatch_async调用中,以免在等待 loadShows 完成时阻塞应用程序的其余部分。

回答by Vasily Bodnarchuk

Details

细节

xCode 9.2, Swift 4

xCode 9.2,斯威夫特 4

Solution

解决方案

class AsyncOperation {

    private let semaphore: DispatchSemaphore
    private let dispatchQueue: DispatchQueue
    typealias CompleteClosure = ()->()

    init(numberOfSimultaneousActions: Int, dispatchQueueLabel: String) {
        semaphore = DispatchSemaphore(value: numberOfSimultaneousActions)
        dispatchQueue = DispatchQueue(label: dispatchQueueLabel)
    }

    func run(closure: @escaping (@escaping CompleteClosure)->()) {
        dispatchQueue.async {
            self.semaphore.wait()
            closure {
                self.semaphore.signal()
            }
        }
    }
}

Usage

用法

let asyncOperation = AsyncOperation(numberOfSimultaneousActions: 1, dispatchQueueLabel: "AnyString")
asyncOperation.run { completeClosure in
    // sync/async action
    // ...


    // action complete        
    completeClosure()
}

Full sample

完整样品

import UIKit

class ViewController: UIViewController {

    let asyncOperation = AsyncOperation(numberOfSimultaneousActions: 1, dispatchQueueLabel: "AnyString")
    var counter = 1

    override func viewDidLoad() {
        super.viewDidLoad()

        let button = UIButton(frame: CGRect(x: 50, y: 50, width: 100, height: 40))
        button.setTitle("Button", for: .normal)
        button.setTitleColor(.blue, for: .normal)
        button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
        view.addSubview(button)

    }

    @objc func buttonTapped() {
        print("Button tapped at: \(Date())")
        asyncOperation.run { completeClosure in
            let counter = self.counter
            print("     - Action \(counter) strat at \(Date())")
            self.counter += 1

            DispatchQueue.global(qos: .background).async {
                sleep(1)
                print("     - Action \(counter) end at \(Date())")
                completeClosure()
            }
        }
    }

}

Results

结果

enter image description here

在此处输入图片说明