等待在Swift中完成异步操作

时间:2014-07-13 16:53:49

标签: ios swift asynchronous

我不知道如何处理这种情况,因为我是iOS开发和Swift的新手。我正在执行数据提取:

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

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

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

3 个答案:

答案 0 :(得分:33)

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

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

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

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

或(清洁方式恕我直言)

添加一个中间completionHandler

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

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

答案 1 :(得分:2)

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

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

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

答案 2 :(得分:0)

详细

xCode 9.2,Swift 4

解决方案

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()
            }
        }
    }
}

用法

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


    // action complete        
    completeClosure()
}

完整样本

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()
            }
        }
    }

}

结果

http://iasecontent.disa.mil/stigs/zip/U_PostgreSQL_9-x_V1R1_STIG.zi