Comment utiliser le fil de fond dans Swift?

329

Comment utiliser le filetage en swift?

dispatchOnMainThread:^{

    NSLog(@"Block Executed On %s", dispatch_queue_get_label(dispatch_get_current_queue()));

}];
Anshul
la source
Quelle partie avez-vous du mal à convertir?
nschum
2
Pourquoi avez-vous ]devant le point-virgule dans la dernière ligne?
akashivskyy
3
il serait utile d'expliquer où vous êtes coincé ou avec qui vous avez besoin d'aide.
nsuinteger
4
Vous devez accepter la bonne réponse si cela vous aide vraiment, cela aidera également les autres à trouver la bonne solution.
Amit Singh
DispatchQueue.global(qos: .background).async { print("Run on background thread") DispatchQueue.main.async { print("We finished that.") // only back on the main thread, may you access UI: label.text = "Done." } }
Anurag Sharma

Réponses:

708

Swift 3.0+

Beaucoup a été modernisé dans Swift 3.0. Exécuter quelque chose sur le thread d'arrière-plan ressemble à ceci:

DispatchQueue.global(qos: .background).async {
    print("This is run on the background queue")

    DispatchQueue.main.async {
        print("This is run on the main queue, after the previous code in outer block")
    }
}

Swift 1,2 à 2,3

let qualityOfServiceClass = QOS_CLASS_BACKGROUND
let backgroundQueue = dispatch_get_global_queue(qualityOfServiceClass, 0)
dispatch_async(backgroundQueue, {
    print("This is run on the background queue")

    dispatch_async(dispatch_get_main_queue(), { () -> Void in
        print("This is run on the main queue, after the previous code in outer block")
    })
})

Pre Swift 1.2 - Problème connu

À partir de Swift 1.1, Apple ne prenait pas en charge la syntaxe ci-dessus sans quelques modifications. Passer QOS_CLASS_BACKGROUNDn'a pas vraiment fonctionné, au lieu de cela utiliser Int(QOS_CLASS_BACKGROUND.value).

Pour plus d'informations, voir la documentation sur les pommes

tobiasdm
la source
23
Et si quelqu'un veut une syntaxe plus Swift, j'ai créé Async qui ajoute du sucre à la syntaxe commeAsync.background {}
tobiasdm
J'utilise votre code dans xCode 6.0.1 et ios 8.Il donne une erreur en tant que classe de retour "QOS_CLASS_BACKGROUND" et il est de type UInt32 et "dispatch_get_global_queue" nécessite un 1er paramètre car int si l'erreur de type arrive.
Zalak Patel
Donc, dans Xcode 6.1.1, je ne reçois pas d'erreur pour utiliser simplement "QOS_CLASS_BACKGROUND". Est-ce réparé?
Lucas Goossen
@LucasGoossen Oui, il a été corrigé. J'ai mis à jour le message en conséquence.
tobiasdm
1
@NikitaPronchik N'est-ce pas clair dans la réponse? Sinon, n'hésitez pas à y apporter des modifications.
tobiasdm
123

La meilleure pratique consiste à définir une fonction réutilisable accessible plusieurs fois.

FONCTION RÉUTILISABLE:

par exemple quelque part comme AppDelegate.swift en tant que fonction globale.

func backgroundThread(_ delay: Double = 0.0, background: (() -> Void)? = nil, completion: (() -> Void)? = nil) {
    dispatch_async(dispatch_get_global_queue(Int(QOS_CLASS_USER_INITIATED.value), 0)) {
        background?()

        let popTime = dispatch_time(DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC)))
        dispatch_after(popTime, dispatch_get_main_queue()) {
            completion?()
        }
    }
}

Remarque: dans Swift 2.0, remplacez QOS_CLASS_USER_INITIATED.value ci-dessus par QOS_CLASS_USER_INITIATED.rawValue à la place

USAGE:

A. Pour exécuter un processus en arrière-plan avec un délai de 3 secondes:

    backgroundThread(3.0, background: {
            // Your background function here
    })

B. Pour exécuter un processus en arrière-plan, puis exécutez une complétion au premier plan:

    backgroundThread(background: {
            // Your function here to run in the background
    },
    completion: {
            // A function to run in the foreground when the background thread is complete
    })

C. Pour retarder de 3 secondes - notez l'utilisation du paramètre d'achèvement sans paramètre d'arrière-plan:

    backgroundThread(3.0, completion: {
            // Your delayed function here to be run in the foreground
    })
Dale Clifford
la source
1
bel extrait, devrait être la bonne réponse. @Dale Clifford
LoVo
Brillante approche Swift-y moderne de haut niveau pour accéder aux méthodes GCD anciennes à partir de la bibliothèque C de bas niveau. Devrait venir standard dans Swift.
Craig Grummitt
2
Très agréable. Veuillez confirmer, le délai ne fonctionne que pour le bloc d'achèvement. Cela signifie donc que le retard dans A. n'a aucun impact et le bloc d'arrière-plan s'exécute immédiatement sans délai.
ObjectiveTC
1
Vous devriez pouvoir remplacer if(background != nil){ background!(); }par background?()une syntaxe un peu plus rapide?
Simon Bengtsson
1
Pourriez-vous mettre à jour ceci pour Swift 3? Le convertisseur automatique l'a transformé en DispatchQueue.global(priority: Int(DispatchQoS.QoSClass.userInitiated.rawValue)).async {mais cela lance une erreur comme cannot invoke initializer for type 'Int' with an argument list of type '(qos_class_t)'. Une solution de travail se trouve ici ( DispatchQueue.global(qos: DispatchQoS.QoSClass.userInitiated).async).
Dev-iL
111

Réponse de Dan Beaulieu dans swift5 (fonctionne également depuis swift 3.0.1).

Swift 5.0.1

extension DispatchQueue {

    static func background(delay: Double = 0.0, background: (()->Void)? = nil, completion: (() -> Void)? = nil) {
        DispatchQueue.global(qos: .background).async {
            background?()
            if let completion = completion {
                DispatchQueue.main.asyncAfter(deadline: .now() + delay, execute: {
                    completion()
                })
            }
        }
    }

}

Usage

DispatchQueue.background(delay: 3.0, background: {
    // do something in background
}, completion: {
    // when background job finishes, wait 3 seconds and do something in main thread
})

DispatchQueue.background(background: {
    // do something in background
}, completion:{
    // when background job finished, do something in main thread
})

DispatchQueue.background(delay: 3.0, completion:{
    // do something in main thread after 3 seconds
})
frouo
la source
Incroyable, merci d'avoir si bien mis à jour le format Swift 3.0.1!
Dale Clifford
1
J'utilise des extensions plus que toute personne vivante. Mais il y a un réel danger à utiliser une extension qui ne diffère pas du tout de l'original!
Fattie
@Frouo Très élégant, est-il possible d'ajouter un gestionnaire de complétion lorsque 4 appels asynchrones sont tous terminés? Je sais que c'est un peu hors sujet.
eonist
1
yup oublier ce lien. tout ce dont vous avez besoin est un groupe de répartition - c'est très très simple; pas de soucis du tout!
Fattie
1
@DilipJangid vous ne pouvez pas, sauf si votre travail dans la backgroundfermeture est très très très long (~ = infini). Cette méthode est conçue pour durer un temps fini: le temps nécessaire à l'exécution de votre tâche d'arrière-plan. Ainsi, la completionfermeture sera appelée dès que le temps d'exécution de votre tâche d'arrière-plan + délai sera écoulé.
frouo
42

Version Swift 3

Swift 3 utilise une nouvelle DispatchQueueclasse pour gérer les files d'attente et les threads. Pour exécuter quelque chose sur le thread d'arrière-plan, vous utiliseriez:

let backgroundQueue = DispatchQueue(label: "com.app.queue", qos: .background)
backgroundQueue.async {
    print("Run on background thread")
}

Ou si vous voulez quelque chose en deux lignes de code:

DispatchQueue.global(qos: .background).async {
    print("Run on background thread")

    DispatchQueue.main.async {
        print("We finished that.")
        // only back on the main thread, may you access UI:
        label.text = "Done."
    }
}

Vous pouvez également obtenir des informations détaillées sur GDC dans Swift 3 dans ce didacticiel .

Dit Sikira
la source
M'a dit. Puisque votre réponse est la meilleure, j'ai jeté une ligne de code montrant comment vous "rappelez lorsque vous avez terminé". N'hésitez pas à vous détendre ou à éditer, cheers
Fattie
35

De Jameson QUAVE tutoriel

Swift 2

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
    //All stuff here
})
alex
la source
3
Juste pour clarification, pourquoi cela serait-il utilisé à la place de la réponse acceptée? S'agit-il simplement d'une ancienne API?
Sirens du
1
@Sirens Je pense que ce serait très utile pour les applications prenant en charge <iOS 8.
bperdue
J'utilise ceci pour iOs 8.2 pour forcer les processus.
μολὼν.λαβέ
DISPATCH_QUEUE_PRIORITY_DEFAULT revient à QOS_CLASS_DEFAULT. Donc je suppose que vous pourriez dire que c'est une syntaxe de plus haut niveau / acceptée.
PostCodeism
34

Dans Swift 4.2 et Xcode 10.1

Nous avons trois types de files d'attente:

1. File d' attente principale : La file d'attente principale est une file d'attente série créée par le système et associée au thread principal de l'application.

2. File d' attente globale : La file d'attente globale est une file d'attente simultanée que nous pouvons demander en fonction de la priorité des tâches.

3. Files d'attente personnalisées: peuvent être créées par l'utilisateur. Les files d'attente simultanées personnalisées sont toujours mappées dans l'une des files d'attente globales en spécifiant une propriété de qualité de service (QoS).

DispatchQueue.main//Main thread
DispatchQueue.global(qos: .userInitiated)// High Priority
DispatchQueue.global(qos: .userInteractive)//High Priority (Little Higher than userInitiated)
DispatchQueue.global(qos: .background)//Lowest Priority
DispatchQueue.global(qos: .default)//Normal Priority (after High but before Low)
DispatchQueue.global(qos: .utility)//Low Priority
DispatchQueue.global(qos: .unspecified)//Absence of Quality

Toutes ces files d'attente peuvent être exécutées de deux manières

1. Exécution synchrone

2. Exécution asynchrone

DispatchQueue.global(qos: .background).async {
    // do your job here
    DispatchQueue.main.async {
        // update ui here
    }
}

//Perform some task and update UI immediately.
DispatchQueue.global(qos: .userInitiated).async {  
    // Perform task
    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()
})

Depuis AppCoda: https://www.appcoda.com/grand-central-dispatch/

//This will print synchronously means, it will print 1-9 & 100-109
func simpleQueues() {
    let queue = DispatchQueue(label: "com.appcoda.myqueue")

    queue.sync {
        for i in 0..<10 {
            print("🔴", i)
        }
    }

    for i in 100..<110 {
        print("Ⓜ️", i)
    }
}

//This will print asynchronously 
func simpleQueues() {
    let queue = DispatchQueue(label: "com.appcoda.myqueue")

    queue.async {
        for i in 0..<10 {
            print("🔴", i)
        }
    }

    for i in 100..<110 {
        print("Ⓜ️", i)
    }
}
iOS
la source
1
Meilleur tutoriel pour les threads medium.com/@gabriel_lewis/…
iOS
Je n'ai vu aucun changement lorsque vous utilisez .background QoS ou .userInitiatedmais pour moi, cela a fonctionné avec.background
rouille
24

Swift 4.x

Mettez ceci dans un fichier:

func background(work: @escaping () -> ()) {
    DispatchQueue.global(qos: .userInitiated).async {
        work()
    }
}

func main(work: @escaping () -> ()) {
    DispatchQueue.main.async {
        work()
    }
}

puis appelez-le là où vous en avez besoin:

background {
     //background job
     main {
       //update UI (or what you need to do in main thread)
     }
}
Hadži Lazar Pešić
la source
22

Vous devez séparer les modifications que vous souhaitez exécuter en arrière-plan des mises à jour que vous souhaitez exécuter sur l'interface utilisateur:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
    // do your task

    dispatch_async(dispatch_get_main_queue()) {
        // update some UI
    }
}
phuongle
la source
dispatch_async(dispatch_get_main_queue()) { // update some UI }Est donc appelé lorsque l'instruction d'arrière-plan (Outer Block) a terminé son exécution?
justColbs
N'est-ce pas uniquement pour Swift 2.3 et inférieur?
Surz
9

De bonnes réponses cependant, de toute façon je veux partager ma solution orientée objet. À jour pour swift 5 .

veuillez le vérifier: AsyncTask

Inspiré conceptuellement par AsyncTask d'Android, j'ai écrit ma propre classe dans Swift

AsyncTask permet une utilisation correcte et facile du thread d'interface utilisateur. Cette classe permet d'effectuer des opérations en arrière-plan et de publier les résultats sur le thread d'interface utilisateur.

Voici quelques exemples d'utilisation

Exemple 1 -

AsyncTask(backgroundTask: {(p:String)->Void in//set BGParam to String and BGResult to Void
        print(p);//print the value in background thread
    }).execute("Hello async");//execute with value 'Hello async'

Exemple 2 -

let task2=AsyncTask(beforeTask: {
           print("pre execution");//print 'pre execution' before backgroundTask
        },backgroundTask:{(p:Int)->String in//set BGParam to Int & BGResult to String
            if p>0{//check if execution value is bigger than zero
               return "positive"//pass String "poitive" to afterTask
            }
            return "negative";//otherwise pass String "negative"
        }, afterTask: {(p:String) in
            print(p);//print background task result
    });
    task2.execute(1);//execute with value 1

Il a 2 types génériques:

  • BGParam - le type du paramètre envoyé à la tâche lors de l'exécution.

  • BGResult - le type du résultat du calcul d'arrière-plan.

    Lorsque vous créez un AsyncTask, vous pouvez attribuer ces types à tout ce dont vous avez besoin pour entrer et sortir de la tâche en arrière-plan, mais si vous n'avez pas besoin de ces types, vous pouvez le marquer comme inutilisé en le définissant simplement sur: Voidou avec une syntaxe plus courte:()

Lorsqu'une tâche asynchrone est exécutée, elle passe par 3 étapes:

  1. beforeTask:()->Void invoquée sur le thread d'interface utilisateur juste avant l'exécution de la tâche.
  2. backgroundTask: (param:BGParam)->BGResult invoqué sur le thread d'arrière-plan immédiatement après
  3. afterTask:(param:BGResult)->Void invoqué sur le thread d'interface utilisateur avec le résultat de la tâche d'arrière-plan
Nikita Kurtin
la source
4
Cela fonctionne à merveille pour moi. Beau travail, pourquoi ne pas le mettre sur github?
36 By Design
8

Puisque la question OP a déjà été répondue ci-dessus, je veux juste ajouter quelques considérations de vitesse:

Je ne recommande pas d'exécuter des tâches avec la priorité de thread .background en particulier sur l'iPhone X où la tâche semble être allouée sur les cœurs de faible puissance.

Voici quelques données réelles d'une fonction de calcul intensif qui lit à partir d'un fichier XML (avec mise en mémoire tampon) et effectue une interpolation des données:

Nom du périphérique / .background / .utility / .default / .userInitiated / .userInteractive

  1. iPhone X: 18,7 s / 6,3 s / 1,8 s / 1,8 s / 1,8 s
  2. iPhone 7: 4,6 s / 3,1 s / 3,0 s / 2,8 s / 2,6 s
  3. iPhone 5s: 7,3 s / 6,1 s / 4,0 s / 4,0 s / 3,8 s

Notez que l'ensemble de données n'est pas le même pour tous les appareils. C'est le plus grand sur l'iPhone X et le plus petit sur l'iPhone 5s.

Cosmin
la source
4

Swift 5

Pour vous faciliter la tâche, créez un fichier "DispatchQueue + Extensions.swift" avec ce contenu:

import Foundation

typealias Dispatch = DispatchQueue

extension Dispatch {

    static func background(_ task: @escaping () -> ()) {
        Dispatch.global(qos: .background).async {
            task()
        }
    }

    static func main(_ task: @escaping () -> ()) {
        Dispatch.main.async {
            task()
        }
    }
}

Utilisation:

Dispatch.background {
    // do stuff

    Dispatch.main { 
        // update UI
    }
}
Xys
la source
2

Grand Central Dispatch est utilisé pour gérer le multitâche dans nos applications iOS.

Vous pouvez utiliser ce code

// Using time interval

DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()+1) {
    print("Hello World")
}

// Background thread
queue.sync {
     for i in 0..<10 {
          print("Hello", i)
     }
}

// Main thread
for i in 20..<30 {
     print("Hello", i)
}

Pour plus d'informations, utilisez ce lien: https://www.programminghub.us/2018/07/07/integrate-dispatcher-in-swift.html

Anil Dhameliya
la source
2

Fonction polyvalente pour le fil

public enum QueueType {
        case Main
        case Background
        case LowPriority
        case HighPriority

        var queue: DispatchQueue {
            switch self {
            case .Main:
                return DispatchQueue.main
            case .Background:
                return DispatchQueue(label: "com.app.queue",
                                     qos: .background,
                                     target: nil)
            case .LowPriority:
                return DispatchQueue.global(qos: .userInitiated)
            case .HighPriority:
                return DispatchQueue.global(qos: .userInitiated)
            }
        }
    }

    func performOn(_ queueType: QueueType, closure: @escaping () -> Void) {
        queueType.queue.async(execute: closure)
    }

Utilisez-le comme:

performOn(.Background) {
    //Code
}
Viral Savaliya
la source
1

J'aime vraiment la réponse de Dan Beaulieu, mais cela ne fonctionne pas avec Swift 2.2 et je pense que nous pouvons éviter ces déballages forcés!

func backgroundThread(delay: Double = 0.0, background: (() -> Void)? = nil, completion: (() -> Void)? = nil) {

    dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0)) {

        background?()

        if let completion = completion{
            let popTime = dispatch_time(DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC)))
            dispatch_after(popTime, dispatch_get_main_queue()) {
                completion()
            }
        }
    }
}
rougeExciter
la source
0
dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), {
    // Conversion into base64 string
    self.uploadImageString =  uploadPhotoDataJPEG.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.EncodingEndLineWithCarriageReturn)
})
AG
la source
-3

dans Swift 4.2 cela fonctionne.

import Foundation

class myThread: Thread
{
    override func main() {
        while(true) {
            print("Running in the Thread");
            Thread.sleep(forTimeInterval: 4);
        }
    }
}

let t = myThread();
t.start();

while(true) {
    print("Main Loop");
    sleep(5);
}
ackers
la source