Ожидание до завершения задачи

Как я могу заставить свой код ждать завершения задачи в DispatchQueue? Нужен ли какой-нибудь CompletionHandler или что-то в этом роде?

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

}

Я использую Xcode 8.2 и записываю в Swift 3.

Ответы

Ответ 1

Используйте DispatchGroup для достижения этой цели. Вы можете получить уведомление, когда групповые вызовы enter() и leave() сбалансированы:

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

или вы можете подождать:

func myFunction() {
    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()

    print(a) // you could also 'return a' here
}

Примечание: group.wait() блокирует текущую очередь (вероятно, основную очередь в вашем случае), поэтому вам нужно dispatch.async в другой очереди (как в примере кода выше), чтобы избежать тупика.

Ответ 2

В Swift 3 нет необходимости в обработчике завершения, когда DispatchQueue завершает одну задачу. Кроме того, вы можете достичь своей цели по-разному.

Один из способов.

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

Он будет ждать завершения цикла, но в этом случае ваш почтовый поток будет блокироваться.

Вы также можете сделать то же самое, что и здесь

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

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

        ////// do your remaining work
    }

Последнее. Если вы хотите использовать completeHandler, когда ваша задача завершается с помощью DispatchQueue, вы можете использовать DispatchWorkItem.

Вот пример использования 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
}

Ответ 3

Использовать группу отправки

   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.

Ответ 4

Swift 4

Вы можете использовать асинхронную функцию для этих ситуаций. При использовании DispatchGroup() иногда возможны тупики.

var a: Int?
@objc func myFunction(completion:@escaping (Bool) -> () ) {

    DispatchQueue.main.async {
        let b: Int = 3
        a = b
        completion(true)
    }

}

override func viewDidLoad() {
    super.viewDidLoad()

    myFunction { (status) in
        if status {
            print(self.a!)
        }
    }
}