私は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行が完了するまで待機させることです。これを処理する最良の方法は何ですか?
非同期関数にハンドラーを渡して後で呼び出す必要があります。
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()
}
これを解決する2つの方法は、両方とも Grand Central Dispatch を使用します(SwiftおよびObjective C)に似ています)。
loadShowsメソッドを同期に変更し、completionHandlerと同じディスパッチキューを使用してから、メソッドの本体全体をdispatch_async;このようにして、メソッド呼び出しはすぐに終了しますが、完了した場合は、同期プログラムのように、loadShowsの後にcompletionHandlerが呼び出されます
gCDセマフォを使用します-言及したBOOLと同じですが、dispatch_semaphore_createで作成されます。 comptionHandlerの前にdispatch_semaphore_waitを呼び出して、セマフォがロック解除されるのを待つ(でロック解除する) dispatch_semaphore_signal); loadShowsの完了を待っている間にアプリの残りの部分をブロックしないように、メソッド本体をdispatch_async呼び出し内に配置することを忘れないでください。
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()
}
}
}
}