ios - library - Espere a que se complete la operación asíncrona en Swift
swift libraries (3)
Detalles
Xcode 9.2, Swift 4
Solución
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()
}
}
}
}
Uso
let asyncOperation = AsyncOperation(numberOfSimultaneousActions: 1, dispatchQueueLabel: "AnyString")
asyncOperation.run { completeClosure in
// sync/async action
// ...
// action complete
completeClosure()
}
Muestra completa
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()
}
}
}
}
Resultados
No estoy seguro de cómo manejar esta situación, ya que soy muy nuevo en el desarrollo de iOS y Swift. Estoy realizando la obtención de datos como así:
func application(application: UIApplication!, performFetchWithCompletionHandler completionHandler: ((UIBackgroundFetchResult) -> Void)!)
{
loadShows()
completionHandler(UIBackgroundFetchResult.NewData)
println("Background Fetch Complete")
}
Mi función loadShows () analiza un montón de datos que obtiene de un sitio web cargado en un UIWebView. El problema es que tengo un temporizador que espera aproximadamente 10 segundos en la función loadShows. Esto permite que el javascript en la página se cargue por completo antes de comenzar a analizar los datos. Mi problema es que el controlador de finalización se completa antes de que lo haga mi loadShows ().
Lo que me gustaría hacer es agregar un bool para "isCompletedParsingShows" y hacer que la línea completadaHandler espere hasta que ese bool sea verdadero. ¿Cuál es la mejor manera de manejar esto?
dos formas de resolver esto, ambas usan Grand Central Dispatch (que es similar en Swift y Objective C):
cambie el método loadShows para hacerlo sincrónico y use la misma cola de envío que el programa FinalizaciónHandler , luego envuelva todo el cuerpo del método en dispatch_async ; De esta manera, la llamada al método finaliza de inmediato, pero se llamará a completedHandler después de loadShows si se termina, como en un programa síncrono
use un semáforo GCD, como el BOOL que menciona, pero creado con dispatch_semaphore_create ; llama a dispatch_semaphore_wait antes de completarHandler para que espere a que se desbloquee el semáforo (desbloquéelo con dispatch_semaphore_signal ); recuerde colocar el cuerpo de su método dentro de una llamada dispatch_async para no tener que bloquear el resto de la aplicación mientras espera a que se complete loadShows.
tienes que pasar tu función asíncrona al controlador para llamar más tarde:
func application(application: UIApplication!, performFetchWithCompletionHandler completionHandler: ((UIBackgroundFetchResult) -> Void)!) {
loadShows(completionHandler)
}
func loadShows(completionHandler: ((UIBackgroundFetchResult) -> Void)!) {
//....
//DO IT
//....
completionHandler(UIBackgroundFetchResult.NewData)
println("Background Fetch Complete")
}
O (forma más limpia IMHO)
añadir una finalización intermediaHandler
func application(application: UIApplication!, performFetchWithCompletionHandler completionHandler: ((UIBackgroundFetchResult) -> Void)!) {
loadShows() {
completionHandler(UIBackgroundFetchResult.NewData)
println("Background Fetch Complete")
}
}
func loadShows(completionHandler: (() -> Void)!) {
//....
//DO IT
//....
completionHandler()
}