web-dev-qa-db-ja.com

非同期操作が完了するまで待つSwift

私はiOS開発とSwiftを初めて使用するため、この状況をどのように処理するのかわかりません。私は次のようにデータを取得しています:

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

私のloadShows()関数は、UIWebViewにロードされたWebサイトから取得した大量のデータを解析します。問題は、loadShows関数に10秒ほど待機するタイマーがあることです。これにより、データの解析を開始する前に、ページ内のJavaScriptを完全にロードできます。私の問題は、loadShows()が完了する前に完了ハンドラーが完了することです。

私がやりたいのは、「isCompletedParsingShows」のブール値を追加し、そのブール値がtrueになるまで、completionHandler行が完了するまで待機させることです。これを処理する最良の方法は何ですか?

23
PretzelJesus

非同期関数にハンドラーを渡して後で呼び出す必要があります。

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()
}
33
Daij-Djan

これを解決する2つの方法は、両方とも Grand Central Dispatch を使用します(SwiftおよびObjective C)に似ています)。

  1. loadShowsメソッドを同期に変更し、completionHandlerと同じディスパッチキューを使用してから、メソッドの本体全体をdispatch_async;このようにして、メソッド呼び出しはすぐに終了しますが、完了した場合は、同期プログラムのように、loadShowsの後にcompletionHandlerが呼び出されます

  2. gCDセマフォを使用します-言及したBOOLと同じですが、dispatch_semaphore_createで作成されます。 comptionHandlerの前にdispatch_semaphore_waitを呼び出して、セマフォがロック解除されるのを待つ(でロック解除する) dispatch_semaphore_signal); loadShowsの完了を待っている間にアプリの残りの部分をブロックしないように、メソッド本体をdispatch_async呼び出し内に配置することを忘れないでください。

2
Alex

詳細

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

}

結果

enter image description here

0