iOS
DispatchGroup
Szukaj…
Wprowadzenie
Wprowadzenie
Załóżmy, że masz uruchomionych wiele wątków. Każdy wątek wykonuje jedno zadanie. Chcesz otrzymywać powiadomienia albo o głównym wątku LUB o innym wątku, gdy wszystkie wątki zadania zostaną zakończone.
Najprostszym rozwiązaniem takiego problemu jest DispatchGroup
.
Korzystając z DispatchGroup
, dla każdego żądania enter
do grupy, a dla każdego ukończonego żądania leave
grupę.
Gdy w grupie nie będzie już żadnych żądań, otrzymasz notify
(powiadomienie).
Stosowanie:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let dispatchGroup = DispatchGroup() //Create a group for the tasks.
let session: URLSession = URLSession.shared
dispatchGroup.enter() //Enter the group for the first task.
let firstTask = session.dataTask(with: URLRequest(url: URL(string: "https://stackoverflow.com")!)) { (data, response, error) in
//Process Response..
dispatchGroup.leave() //Leave the group for the first task.
}
dispatchGroup.enter() //Enter the group for the second task.
let secondTask = session.dataTask(with: URLRequest(url: URL(string: "https://google.ca")!)) { (data, response, error) in
//Process Response..
dispatchGroup.leave() //Leave the group for the second task.
}
//Get notified on the main thread/queue.. when ALL of the tasks above has been completed.
dispatchGroup.notify(queue: DispatchQueue.main) {
print("Every task is complete")
}
//Start the tasks.
firstTask.resume()
secondTask.resume()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
W związku z powyższym nie musisz wait
nieskończoność, aż wszystkie zadania zostaną zakończone. Możesz wyświetlić moduł ładujący PRZED wszystkimi zadaniami uruchomionymi i zamknąć moduł ładujący PO zakończeniu wszystkich zadań. W ten sposób główny wątek nie zostanie zablokowany, a kod pozostanie czysty.
Załóżmy teraz, że chcesz, aby zadania były również ordered
lub sekwencyjnie dodawano ich odpowiedzi do tablicy. Możesz wykonać następujące czynności:
import UIKit
//Locking mechanism..
func synchronized(_ lock: AnyObject, closure: () -> Void) {
objc_sync_enter(lock)
closure()
objc_sync_exit(lock)
}
class ViewController: UIViewController {
let lock = NSObject() //Object to lock on.
var responseArray = Array<Data?>() //Array of responses.
override func viewDidLoad() {
super.viewDidLoad()
let dispatchGroup = DispatchGroup()
let session: URLSession = URLSession.shared
dispatchGroup.enter() //Enter the group for the first task.
let firstTask = session.dataTask(with: URLRequest(url: URL(string: "https://stackoverflow.com")!)) { (data, response, error) in
//Process Response..
synchronized(self.lock, closure: { () -> Void in
self.responseArray[0] = data ?? nil
})
dispatchGroup.leave() //Leave the group for the first task.
}
dispatchGroup.enter() //Enter the group for the second task.
let secondTask = session.dataTask(with: URLRequest(url: URL(string: "https://google.ca")!)) { (data, response, error) in
//Process Response..
synchronized(self.lock, closure: { () -> Void in
self.responseArray[1] = data ?? nil
})
dispatchGroup.leave() //Leave the group for the second task.
}
//Get notified on the main thread.. when ALL of the requests above has been completed.
dispatchGroup.notify(queue: DispatchQueue.main) {
print("Every task is complete..")
for i in 0..<self.responseArray.count {
if self.responseArray[i] == nil {
print("Request #\(i) Failed.\n")
}
else {
print("Request #\(i) Succeeded.\n")
}
}
}
//Two tasks added to the array. Responses are assumed nil until they complete.
self.responseArray.append(nil)
self.responseArray.append(nil)
//Start the tasks.
firstTask.resume()
secondTask.resume()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
Notatki
Każdy wpis musi mieć wyjście w grupie DispatchGroup
. Jeśli zapomnisz leave
po entering
, konfigurujesz się. NIGDY nie będziesz powiadamiany o zakończeniu zadań.
Ilość enter
musi być równa ilości leave
.