22

Jak mogę sprawić, że mój kod będzie czekał do zakończenia zadania w punkcie DispatchQueue? Czy potrzebuje jakiegokolwiek narzędzia CompletionHandler lub czegoś takiego?Oczekiwanie na zakończenie zadania

func myFunction() { 
    var a: Int? 

    DispatchQueue.main.async { 
     var b: Int = 3 
     a = b 
    } 

    // wait until the task finishes, then print 

    print(a) // - this will contain nil, of course, because it 
      // will execute before the code above 

} 

Używam Xcode 8.2 i pisania w Swift 3.

Odpowiedz

59

Zastosowanie DispatchGroup s do osiągnięcia tego celu. Możesz też otrzymywać powiadomienia, gdy grupa jest enter() i leave() rozmowy są wyważone:

func myFunction() { 
    var a: Int? 

    let group = DispatchGroup() 
    group.enter() 

    DispatchQueue.main.async { 
     a = 1 
     group.leave() 
    } 

    // does not wait. But the code in notify() gets run 
    // after enter() and leave() calls are balanced 

    group.notify(queue: .main) { 
     print(a) 
    } 
} 

czy można czekać (i powrót):

func myFunction() -> Int? { 
    var a: Int? 

    let group = DispatchGroup() 
    group.enter() 

    // avoid deadlocks by not using .main queue here 
    DispatchQueue.global(attributes: .qosDefault).async { 
     a = 1 
     group.leave() 
    } 

    // wait ... 
    group.wait() 

    // ... and return as soon as "a" has a value 
    return a 
} 

Uwaga: group.wait() blokuje prąd kolejka (prawdopodobnie główna kolejka w twoim przypadku), więc musisz wywołać.async w innej kolejce (jak w powyższym kodzie przykładowym), aby uniknąć impasu .

+0

Chcę wykonać funkcję w innej klasie, ale chcę poczekać, aby zakończyć tę funkcję, a następnie w obecnej klasie nadal, w jaki sposób mogę obsłużyć to? –

+1

@SaeedRahmatolahi: Użyj metody 'wait' (jeśli nie ma problemu z zablokowaniem, tzn. Jeśli nie znajdujesz się w głównym wątku) lub podaj procedurę obsługi zakończenia lub użyj metody powiadamiania w klasie wywołującej. – shallowThought

+0

Dlaczego nazywacie 'group.enter' poza blokiem async? Czy nie powinien to być każdy blok odpowiedzialny za wchodzenie i opuszczanie grupy? – Bill

1

Zastosowania, wysyłkowy

dispatchGroup.enter() 
    FirstOperation(completion: { _ in 
dispatchGroup.leave() 
    }) 
    dispatchGroup.enter() 
    SecondOperation(completion: { _ in 
dispatchGroup.leave() 
    }) 
    dispatchGroup.wait() //Waits here on this thread until the two operations complete executing. 
+1

Zakładając, że nazywają to na głównej kolejce, będzie to prowadzić do impasu. – shallowThought

+0

@shallowThought Tak true. – Prateekro

+0

Chcę wykonać funkcję w innej klasie, ale chcę poczekać, aby zakończyć tę funkcję, a następnie w obecnej klasie nadal, w jaki sposób można obsłużyć? –

11

W Swift 3 nie ma potrzeby obsługi zakończenia, gdy DispatchQueue kończy jedno zadanie. Co więcej, możesz osiągnąć swój cel na różne sposoby.

Jednym ze sposobów jest to.

var a: Int? 

    let queue = DispatchQueue(label: "com.app.queue") 
    queue.sync { 

     for i in 0..<10 { 

      print("Ⓜ️" , i) 
      a = i 
     } 
    } 

    print("After Queue \(a)") 

Będzie czekać, aż pętla się zakończy, ale w tym przypadku wątek poczty zostanie zablokowany.

Można również zrobić to samo jak ten

let myGroup = DispatchGroup() 
    myGroup.enter() 
    //// Do your task 

    myGroup.leave() //// When your task completes 
    myGroup.notify(queue: DispatchQueue.main) { 

     ////// do your remaining work 
    } 

ostatnia sprawa. Jeśli chcesz użyć funkcji completionHandler, gdy zadanie zakończy się za pomocą DispatchQueue, możesz użyć DispatchWorkItem.

Oto przykład jak używać DispatchWorkItem

let workItem = DispatchWorkItem { 
    // Do something 
} 

let queue = DispatchQueue.global() 
queue.async { 
    workItem.perform() 
} 
workItem.notify(queue: DispatchQueue.main) { 
    // Here you can notify you Main thread 
}