Je ne suis pas sûr de savoir comment gérer cette situation car je suis très novice dans le développement iOS et Swift. J'effectue l'extraction des données comme suit:
func application(application: UIApplication!, performFetchWithCompletionHandler completionHandler: ((UIBackgroundFetchResult) -> Void)!)
{
loadShows()
completionHandler(UIBackgroundFetchResult.NewData)
println("Background Fetch Complete")
}
Ma fonction loadShows () analyse un ensemble de données provenant d'un site Web chargé dans un UIWebView. Le problème est que j'ai une minuterie qui attend environ 10 secondes dans la fonction loadShows. Cela permet au javascript de la page de se charger complètement avant que je commence à analyser les données. Mon problème est que le gestionnaire d'achèvement se termine avant mon loadShows ().
Ce que j'aimerais faire, c'est ajouter une valeur bool pour "isCompletedParsingShows" et faire en sorte que la ligne completionHandler attende jusqu'à ce que cette valeur bool soit vraie. Quelle est la meilleure façon de gérer cela?
vous devez transmettre votre fonction asynchrone au gestionnaire pour appeler plus tard:
func application(application: UIApplication!, performFetchWithCompletionHandler completionHandler: ((UIBackgroundFetchResult) -> Void)!) {
loadShows(completionHandler)
}
func loadShows(completionHandler: ((UIBackgroundFetchResult) -> Void)!) {
//....
//DO IT
//....
completionHandler(UIBackgroundFetchResult.NewData)
println("Background Fetch Complete")
}
ajouter un AchèvementHandler intermédiaire
func application(application: UIApplication!, performFetchWithCompletionHandler completionHandler: ((UIBackgroundFetchResult) -> Void)!) {
loadShows() {
completionHandler(UIBackgroundFetchResult.NewData)
println("Background Fetch Complete")
}
}
func loadShows(completionHandler: (() -> Void)!) {
//....
//DO IT
//....
completionHandler()
}
deux façons de résoudre ce problème, les deux utilisent Grand Central Dispatch (qui est similaire dans Swift et Objective C):
changez la méthode loadShows pour la rendre synchrone et utilisez la même file d'attente de dispatch que completionHandler, puis enveloppez le corps entier de la méthode dans un dispatch_async; Ainsi, l'appel à la méthode se termine immédiatement, mais d'achèvement -Handler sera appelé après loadShows si elle est terminée, comme dans un programme synchrone.
utilisez un sémaphore GCD - comme le BOOL que vous avez mentionné, mais créé avec dispatch_semaphore_create; vous appelez dispatch_semaphore_wait avant completionHandler pour le faire attendre que le sémaphore soit déverrouillé (déverrouillez-le avec dispatch_semaphore_signal); N'oubliez pas de placer votre corps de méthode dans un appel dispatch_async afin de ne pas le laisser bloquer le reste de l'application en attendant que loadShows soit terminé.
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()
}
}
}
}