Wie verwende ich Hintergrund-Thread in Swift?

328

Wie verwende ich Threading in Swift?

dispatchOnMainThread:^{

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

}];
Anshul
quelle
Welchen Teil haben Sie Probleme beim Konvertieren?
Nschum
2
Warum haben Sie ]vor dem Semikolon in der letzten Zeile?
Akashivskyy
3
Es wäre hilfreich, wenn Sie erklären, wo Sie stecken bleiben oder bei denen Sie Hilfe benötigen.
nsuinteger
4
Sie müssen die richtige Antwort akzeptieren, wenn es Ihnen wirklich hilft, es wird auch anderen helfen, die richtige Lösung zu finden.
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

Antworten:

707

Swift 3.0+

In Swift 3.0 wurde viel modernisiert . Das Ausführen von etwas im Hintergrund-Thread sieht folgendermaßen aus:

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 bis 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 - Bekanntes Problem

Ab Swift 1.1 hat Apple die obige Syntax ohne einige Änderungen nicht unterstützt. Übergeben QOS_CLASS_BACKGROUNDhat nicht wirklich funktioniert, sondern verwendet Int(QOS_CLASS_BACKGROUND.value).

Weitere Informationen finden Sie in der Dokumentation zu Äpfeln

Tobiasdm
quelle
23
Und wenn jemand eine schnellere Syntax möchte, habe ich Async erstellt , das der Syntax etwas Zucker hinzufügt, wieAsync.background {}
tobiasdm
Ich verwende Ihren Code in xCode 6.0.1 und ios 8. Es gibt einen Fehler als "QOS_CLASS_BACKGROUND" -Rückgabeklasse und es ist vom Typ UInt32 und "dispatch_get_global_queue" erfordert den ersten Parameter, da int ein Typfehler auftritt.
Zalak Patel
In Xcode 6.1.1 wird also kein Fehler angezeigt, wenn ich einfach "QOS_CLASS_BACKGROUND" verwende. Ist es behoben?
Lucas Goossen
@LucasGoossen Ja, es wurde behoben. Ich habe den Beitrag entsprechend aktualisiert.
Tobiasdm
1
@NikitaPronchik Ist das nicht aus der Antwort klar? Andernfalls können Sie Änderungen daran vornehmen.
Tobiasdm
122

Die beste Vorgehensweise besteht darin, eine wiederverwendbare Funktion zu definieren, auf die mehrmals zugegriffen werden kann.

WIEDERVERWENDBARE FUNKTION:

zB irgendwo wie AppDelegate.swift als globale Funktion.

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?()
        }
    }
}

Hinweis: in Swift 2.0 ersetzen QOS_CLASS_USER_INITIATED.value oben mit QOS_CLASS_USER_INITIATED.rawValue statt

VERWENDUNGSZWECK:

A. So führen Sie einen Prozess im Hintergrund mit einer Verzögerung von 3 Sekunden aus:

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

B. Um einen Prozess im Hintergrund auszuführen, führen Sie eine Vervollständigung im Vordergrund aus:

    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. Um 3 Sekunden zu verzögern - beachten Sie die Verwendung des Abschlussparameters ohne Hintergrundparameter:

    backgroundThread(3.0, completion: {
            // Your delayed function here to be run in the foreground
    })
Dale Clifford
quelle
1
schönes schnipsel, sollte die richtige antwort sein. @ Dale Clifford
LoVo
Genialer moderner Swift-y-Ansatz auf hoher Ebene für den Zugriff auf alte GCD-Methoden aus der C-Bibliothek auf niedriger Ebene. Sollte in Swift Standard sein.
Craig Grummitt
2
Sehr schön. Würden Sie bitte bestätigen, dass die Verzögerung nur für den Abschlussblock funktioniert. Das bedeutet also, dass die Verzögerung in A. keine Auswirkung hat und der Hintergrundblock sofort ohne Verzögerung ausgeführt wird.
ObjectiveTC
1
Sie sollten ersetzen können if(background != nil){ background!(); }mit background?()einer etwas swiftier Syntax?
Simon Bengtsson
1
Könnten Sie dies bitte für Swift 3 aktualisieren? Der Autokonverter hat es in DispatchQueue.global(priority: Int(DispatchQoS.QoSClass.userInitiated.rawValue)).async {aber dies wirft einen Fehler wie cannot invoke initializer for type 'Int' with an argument list of type '(qos_class_t)'. Eine funktionierende Lösung finden Sie hier ( DispatchQueue.global(qos: DispatchQoS.QoSClass.userInitiated).async).
Dev-iL
110

Dan Beaulieus Antwort in swift5 (funktioniert auch seit 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()
                })
            }
        }
    }

}

Verwendungszweck

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
quelle
Erstaunlich, danke, dass Sie so gut auf das Swift 3.0.1-Format aktualisiert haben!
Dale Clifford
1
Ich benutze Erweiterungen mehr als jede lebende Person. Es besteht jedoch eine echte Gefahr, eine Erweiterung zu verwenden, die sich überhaupt nicht vom Original unterscheidet!
Fattie
@Frouo Sehr elegant, ist es möglich, einen Abschluss-Handler hinzuzufügen, wenn 4 asynchrone Aufrufe alle beendet sind? Ich weiß, dass es ein bisschen vom Thema abweicht.
Eonist
1
yup vergiss diesen Link. Alles, was Sie brauchen, ist eine Versandgruppe - es ist sehr, sehr einfach; Keine Sorge!
Fattie
1
@ DilipJangid können Sie nicht, es sei denn, Ihr Job in der backgroundSchließung ist sehr sehr sehr lang (~ = unendlich). Diese Methode ist auf eine begrenzte Zeit ausgelegt: die Zeit, die Ihr Hintergrundjob zur Ausführung benötigt. Der completionAbschluss wird also aufgerufen, sobald die Ausführungszeit + Verzögerung für den Hintergrundjob abgelaufen ist.
Frouo
42

Swift 3 Version

Swift 3 verwendet eine neue DispatchQueueKlasse zum Verwalten von Warteschlangen und Threads. Um etwas im Hintergrund-Thread auszuführen, würden Sie Folgendes verwenden:

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

Oder wenn Sie etwas in zwei Codezeilen wollen:

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."
    }
}

In diesem Tutorial erhalten Sie auch ausführliche Informationen zu GDC in Swift 3 .

Sagte Sikira
quelle
Sagte. Da Ihre Antwort die beste ist, habe ich eine Codezeile eingefügt, die zeigt, wie Sie "zurückrufen, wenn Sie fertig sind". Fühlen Sie sich frei, sich zu entspannen oder zu bearbeiten, Prost
Fattie
35

Aus dem Tutorial von Jameson Quave

Swift 2

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
    //All stuff here
})
Alex
quelle
3
Warum sollte dies nur zur Verdeutlichung anstelle der akzeptierten Antwort verwendet werden? Ist das nur eine ältere API?
Sirenen
1
@ Sirens Ich würde denken, dass dies sehr nützlich für Apps wäre, die <iOS 8 unterstützen.
bperdue
Ich benutze dies für iOs 8.2, um Prozesse zu erzwingen.
μολὼν.λαβέ
DISPATCH_QUEUE_PRIORITY_DEFAULT wird auf QOS_CLASS_DEFAULT zurückgesetzt. Man könnte also sagen, es ist eine höhere / akzeptierte Syntax.
PostCodeism
34

In Swift 4.2 und Xcode 10.1

Wir haben drei Arten von Warteschlangen:

1. Hauptwarteschlange: Die Hauptwarteschlange ist eine serielle Warteschlange, die vom System erstellt und dem Hauptthread der Anwendung zugeordnet wird.

2. Globale Warteschlange: Die globale Warteschlange ist eine gleichzeitige Warteschlange, die wir in Bezug auf die Priorität der Aufgaben anfordern können.

3. Benutzerdefinierte Warteschlangen: können vom Benutzer erstellt werden. Benutzerdefinierte gleichzeitige Warteschlangen werden immer einer der globalen Warteschlangen zugeordnet, indem eine Quality of Service-Eigenschaft (QoS) angegeben wird.

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

Diese alle Warteschlangen können auf zwei Arten ausgeführt werden

1. Synchrone Ausführung

2. Asynchrone Ausführung

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()
})

Von 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
quelle
1
Bestes Tutorial für Threads medium.com/@gabriel_lewis/…
iOS
Ich habe keine Änderungen gesehen, wenn Sie .background QoS verwenden oder .userInitiatedaber für mich hat es geklappt mit.background
Rost
24

Swift 4.x.

Legen Sie dies in eine Datei:

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

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

und nennen Sie es dann, wo Sie brauchen:

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

Sie müssen die Änderungen, die Sie im Hintergrund ausführen möchten, von den Updates trennen, die Sie auf der Benutzeroberfläche ausführen möchten:

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

    dispatch_async(dispatch_get_main_queue()) {
        // update some UI
    }
}
Phuongle
quelle
Wird dispatch_async(dispatch_get_main_queue()) { // update some UI }also aufgerufen, wenn die Hintergrundanweisung (Outer Block) ausgeführt wird?
JustColbs
Ist das nicht nur für Swift 2.3 und niedriger?
Surz
9

Gute Antworten, trotzdem möchte ich meine objektorientierte Lösung teilen. Aktuell für Swift 5 .

Bitte probieren Sie es aus: AsyncTask

Konzeptionell inspiriert von Androids AsyncTask, habe ich meine eigene Klasse in Swift geschrieben

AsyncTask ermöglicht die ordnungsgemäße und einfache Verwendung des UI-Threads. Diese Klasse ermöglicht das Ausführen von Hintergrundoperationen und das Veröffentlichen von Ergebnissen im UI-Thread.

Hier sind einige Anwendungsbeispiele

Beispiel 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'

Beispiel 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

Es hat 2 generische Typen:

  • BGParam - Der Typ des Parameters, der bei der Ausführung an die Task gesendet wird.
  • BGResult - die Art des Ergebnisses der Hintergrundberechnung.

    Wenn Sie eine AsyncTask erstellen, können Sie diese Typen an alles übergeben, was Sie zum Ein- und Ausgeben der Hintergrundaufgabe benötigen. Wenn Sie diese Typen jedoch nicht benötigen, können Sie sie als unbenutzt markieren, indem Sie sie einfach auf Folgendes setzen: Voidoder mit einer kürzeren Syntax:()

Wenn eine asynchrone Aufgabe ausgeführt wird, werden drei Schritte ausgeführt:

  1. beforeTask:()->Void Wird im UI-Thread aufgerufen, kurz bevor die Aufgabe ausgeführt wird.
  2. backgroundTask: (param:BGParam)->BGResult wird unmittelbar danach im Hintergrund-Thread aufgerufen
  3. afterTask:(param:BGResult)->Void Wird im UI-Thread mit dem Ergebnis der Hintergrundaufgabe aufgerufen
Nikita Kurtin
quelle
4
Das funktioniert wunderbar für mich. Gute Arbeit, warum nicht auf Github setzen?
36 By Design
8

Da die OP-Frage bereits oben beantwortet wurde, möchte ich nur einige Überlegungen zur Geschwindigkeit hinzufügen:

Ich empfehle nicht, Aufgaben mit der Priorität .background thread auszuführen, insbesondere auf dem iPhone X, wo die Aufgabe auf den Kernen mit geringem Stromverbrauch zugewiesen zu sein scheint.

Hier sind einige reale Daten aus einer rechenintensiven Funktion, die aus einer XML-Datei (mit Pufferung) liest und Dateninterpolation durchführt:

Gerätename / .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,3s / 6,1s / 4,0s / 4,0s / 3,8s

Beachten Sie, dass der Datensatz nicht für alle Geräte gleich ist. Es ist das größte auf dem iPhone X und das kleinste auf dem iPhone 5s.

Cosmin
quelle
4

Swift 5

Erstellen Sie zur Vereinfachung eine Datei "DispatchQueue + Extensions.swift" mit folgendem Inhalt:

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()
        }
    }
}

Verwendungszweck :

Dispatch.background {
    // do stuff

    Dispatch.main { 
        // update UI
    }
}
Xys
quelle
2

Grand Central Dispatch wird für Multitasking in unseren iOS-Apps verwendet.

Sie können diesen Code verwenden

// 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)
}

Weitere Informationen erhalten Sie über diesen Link: https://www.programminghub.us/2018/07/integrate-dispatcher-in-swift.html

Anil Dhameliya
quelle
2

Mehrzweckfunktion für Thread

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)
    }

Verwenden Sie es wie:

performOn(.Background) {
    //Code
}
Virale Savaliya
quelle
1

Ich mag die Antwort von Dan Beaulieu sehr, aber sie funktioniert nicht mit Swift 2.2 und ich denke, wir können diese fiesen erzwungenen Auspackungen vermeiden!

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
quelle
0
dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), {
    // Conversion into base64 string
    self.uploadImageString =  uploadPhotoDataJPEG.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.EncodingEndLineWithCarriageReturn)
})
AG
quelle
-3

In Swift 4.2 funktioniert dies.

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
quelle