Dans Swift, comment appeler une méthode avec des paramètres sur le fil principal de GCD?

194

Dans mon application, j'ai une fonction qui crée une NSRURLSession et envoie une NSURLRequest en utilisant

sesh.dataTaskWithRequest(req, completionHandler: {(data, response, error)

Dans le bloc d'achèvement de cette tâche, je dois faire un calcul qui ajoute une UIImage au viewcontroller appelant. J'ai un func appelé

func displayQRCode(receiveAddr, withAmountInBTC:amountBTC)

qui effectue le calcul d'ajout d'UIImage. Si j'essaie d'exécuter le code d'ajout de vue à l'intérieur du bloc de complétion, Xcode génère une erreur indiquant que je ne peux pas utiliser le moteur de mise en page pendant un processus en arrière-plan. J'ai donc trouvé du code sur SO qui tente de mettre en file d'attente une méthode sur le thread principal:

let time = dispatch_time(DISPATCH_TIME_NOW, Int64(0.0 * Double(NSEC_PER_MSEC)))

dispatch_after(time, dispatch_get_main_queue(), {
    let returned = UIApplication.sharedApplication().sendAction("displayQRCode:", to: self.delegate, from: self, forEvent: nil)
})

Cependant, je ne sais pas comment ajouter les paramètres "receiveAddr" et "amountBTC" à cet appel de fonction. Comment faire cela, ou quelqu'un peut-il suggérer un moyen optimal d'ajouter un appel de méthode à la file d'attente principale de l'application?

Almel
la source

Réponses:

502

Les versions modernes de Swift utilisent DispatchQueue.main.asyncpour envoyer au thread principal:

DispatchQueue.main.async { 
  // your code here
}

Pour envoyer après sur la file d'attente principale, utilisez:

DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
  // your code here
}

Anciennes versions de Swift utilisées:

dispatch_async(dispatch_get_main_queue(), {
  let delegateObj = UIApplication.sharedApplication().delegate as YourAppDelegateClass
  delegateObj.addUIImage("yourstring")
})
codester
la source
Bien que vous ayez raison de dire que votre suggestion fonctionne, je pense que ma réponse est légèrement meilleure car elle ne fait pas d'appel à UIApplication.sharedApplication, ce qui est inhabituel et pourrait rejeter d'autres lecteurs de mon code. La portée de ma réponse est limitée aux objets d'importance, tandis que la vôtre apporte des objets auxiliaires qui m'obligent à lire plus de documents pour savoir exactement ce que je fais. Et j'ai modifié ma question d'origine pour contenir l'appel de fonction correct. J'avais pensé que le displayQRCode n'était pas assez précis, mais avec nos commentaires maintenant, il l'est. Merci d'avoir fait remarquer cela.
almel
85

Version Swift 3+ et Swift 4:

DispatchQueue.main.async {
    print("Hello")
}

Swift 3 et Xcode 9.2:

dispatch_async_on_main_queue {
    print("Hello")
}
DazChong
la source
15

Swift 2

En utilisant les fermetures de fuite, cela devient:

dispatch_async(dispatch_get_main_queue()) {
    self.tableView.reloadData()
}

Trailing Closures est un sucre syntaxique Swift qui permet de définir la fermeture en dehors de la portée du paramètre de fonction. Pour plus d'informations, consultez Fermetures de fin dans le Guide du langage de programmation Swift 2.2.

Dans le cas de dispatch_async, l'API est func dispatch_async(queue: dispatch_queue_t, _ block: dispatch_block_t)depuis dispatch_block_tun alias de type pour () -> Void- Une fermeture qui reçoit 0 paramètre et n'a pas de valeur de retour, et le bloc étant le dernier paramètre de la fonction, nous pouvons définir la fermeture dans la portée externe de dispatch_async.

Maxim Veksler
la source
1
c'était exactement les 3 lignes que je cherchais ... maintenant vous pouvez arrêter de lire dans mes pensées
Laszlo
8

Recharger la collectionAfficher sur le fil principal

DispatchQueue.main.async {
    self.collectionView.reloadData()
}
Sanjay Mali
la source
7

Voici la meilleure syntaxe de style Swifty / Cocoa (IMO) pour obtenir le même résultat que les autres réponses:

NSOperationQueue.mainQueue().addOperationWithBlock({
    // Your code here
})

Ou vous pouvez récupérer la bibliothèque Async Swift populaire pour encore moins de code et plus de fonctionnalités:

Async.main {
    // Your code here
}
pejalo
la source
méthode renomméeOperationQueue.main.addOperation({ }
Frostmourne
3

La bonne façon de faire est d'utiliser dispatch_async dans la main_queue, comme je l'ai fait dans le code suivant

dispatch_async(dispatch_get_main_queue(), {
    (self.delegate as TBGQRCodeViewController).displayQRCode(receiveAddr, withAmountInBTC:amountBTC)
})
Almel
la source
2

Voici une jolie petite fonction globale que vous pouvez ajouter pour une syntaxe plus agréable:

func dispatch_on_main(block: dispatch_block_t) {
    dispatch_async(dispatch_get_main_queue(), block)
}

Et utilisation

dispatch_on_main {
    // Do some UI stuff
}
Mateusz Grzegorzek
la source
2
//Perform some task and update UI immediately.
DispatchQueue.global(qos: .userInitiated).async {  
    // Call your function here
    DispatchQueue.main.async {  
        // Update UI
        self.tableView.reloadData()  
    }
}

//To call or execute function after some time
DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
    //Here call your function
}

//If you want to do changes in UI use this
DispatchQueue.main.async(execute: {
    //Update UI
    self.tableView.reloadData()
})
iOS
la source
2

N'oubliez pas de vous affaiblir si vous vous utilisez à l'intérieur de la fermeture.

dispatch_async(dispatch_get_main_queue(),{ [weak self] () -> () in
    if let strongSelf = self {
        self?.doSomething()
    }
})
villy393
la source
1
Pouvez-vous expliquer pourquoi nous devrions faire cela?
Jackspicer
C'est parce qu'il peut créer des cycles de mémoire - c'est-à-dire que j'ai une forte référence à quelque chose et qu'il a une forte référence à moi. Cela signifie qu'aucun de nous ne peut quitter le tas de mémoire.
jackofallcode