Как я могу отправить dispatch_sync, dispatch_async, dispatch_after и т. Д. В Swift 3, Swift 4 и далее?

243

У меня есть много кода в проектах Swift 2.x (или даже 1.x), который выглядит следующим образом:

// Move to a background thread to do some long running work
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
    let image = self.loadOrGenerateAnImage()
    // Bounce back to the main thread to update the UI
    dispatch_async(dispatch_get_main_queue()) {
        self.imageView.image = image
    }
}

Или что-то вроде этого, чтобы задержать выполнение:

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(0.5 * Double(NSEC_PER_SEC))), dispatch_get_main_queue()) {
    print("test")
}

Или любое другое использование API Grand Central Dispatch ...

Теперь, когда я открыл свой проект в Xcode 8 (бета) для Swift 3, я получаю всевозможные ошибки. Некоторые из них предлагают исправить мой код, но не все исправления создают рабочий код. Что мне делать с этим?

rickster
источник
Ответили здесь: stackoverflow.com/questions/37805885/…
t1ser

Ответы:

343

С самого начала Swift предоставил некоторые возможности для того, чтобы сделать ObjC и C более Swifty, добавляя больше с каждой версией. Теперь в Swift 3 новая функция «импортировать как член» позволяет интегрированным средам с определенными стилями C API - где у вас есть тип данных, который работает как класс, и набор глобальных функций для работы с ним - действовать больше как Swift-нативные API. Типы данных импортируются как классы Swift, связанные с ними глобальные функции импортируются как методы и свойства этих классов, и некоторые связанные вещи, такие как наборы констант, могут стать подходящими подтипами.

В бета-версии Xcode 8 / Swift 3 Apple применила эту функцию (наряду с несколькими другими), чтобы сделать среду Dispatch намного более быстрой. (И Core Graphics тоже.) Если вы следили за усилиями Swift с открытым исходным кодом, это не новость , но теперь это первый раз, когда он является частью Xcode.

Ваш первый шаг по перемещению любого проекта в Swift 3 должен состоять в том, чтобы открыть его в Xcode 8 и выбрать Edit> Convert> To Current Swift Syntax ... в меню. Это применимо (с вашим рассмотрением и одобрением) ко всем изменениям, необходимым сразу для всех переименованных API и других изменений. (Зачастую строка кода затрагивается более чем одним из этих изменений одновременно, поэтому реагирование на исправление ошибки может привести к неправильной обработке в отдельности.)

В результате общий шаблон для отскока работы от фона и обратно теперь выглядит так:

// Move to a background thread to do some long running work
DispatchQueue.global(qos: .userInitiated).async {
    let image = self.loadOrGenerateAnImage()
    // Bounce back to the main thread to update the UI
    DispatchQueue.main.async {
        self.imageView.image = image
    }
}

Обратите внимание, что мы используем .userInitiatedвместо одной из старых DISPATCH_QUEUE_PRIORITYконстант. Спецификаторы качества обслуживания (QoS) были введены в OS X 10.10 / iOS 8.0, предоставляя более ясный способ для системы расставить приоритеты в работе и исключить старые спецификаторы приоритетов. Подробности смотрите в документации Apple по фоновой работе и энергоэффективности .

Кстати, если вы держать свои собственные очереди , чтобы организовать работу, так , чтобы получить один теперь выглядит следующим образом (обратите внимание , что DispatchQueueAttributesявляется OptionSet, поэтому можно использовать литералы коллекции в стиле комбинировать варианты):

class Foo { 
    let queue = DispatchQueue(label: "com.example.my-serial-queue",
                           attributes: [.serial, .qosUtility])
    func doStuff() {
        queue.async {
            print("Hello World")
        }
    }
}

Используете, dispatch_afterчтобы сделать работу позже? Это также метод для очередей, и он требует DispatchTime, который имеет операторы для различных числовых типов, так что вы можете просто добавить целые или дробные секунды:

DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { // in half a second...
    print("Are we there yet?")
}

Вы можете обойти новый Диспетчерский API, открыв его интерфейс в Xcode 8 - используйте Open Quickly, чтобы найти модуль Dispatch, или поместите символ (например DispatchQueue) в ваш проект / игровую площадку Swift и щелкните по нему командой, а затем бегите вокруг модуль оттуда. (Вы можете найти Swift Dispatch API на изящном новом Справочном веб-сайте API Reference и in-Xcode doc viewer, но похоже, что содержимое документа из версии C пока еще не перемещено в него.)

См. Руководство по миграции для получения дополнительных советов.

rickster
источник
3
Что касается Xcode 8 Beta 6, атрибут .serial пропал и поведение по умолчанию - forums.developer.apple.com/message/159457#159457
hyouuu
6
Это требует обновления с XCode 8.1 .. метка атрибутов исчезла, и вместо нее мы можем использовать 'DispatchQueue.global (qos: .background) .async'
Mike M
2
Прекрасный ответ. Действительно помог мне разобраться с этим.
Мохсин Хубайб Ахмед
Я должен был использовать qos:вместоattributes:
Ислам Q.
Разве это не должно быть myQueue.async {в class Fooпримере?
Vacawama
142

В Xcode 8 beta 4 не работает ...

Использование:

DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
    print("Are we there yet?")
}

для асинхронности два пути:

DispatchQueue.main.async {
    print("Async1")
}

DispatchQueue.main.async( execute: {
    print("Async2")
})
ingconti
источник
Так что это не блокирует интерфейс?
user25
72

Это один хороший пример Swift 4о async:

DispatchQueue.global(qos: .background).async {
    // Background Thread
    DispatchQueue.main.async {
        // Run UI Updates or call completion block
    }
}
С. Мацепура
источник
hi DispatchQueue.main.async {// Запуск обновлений пользовательского интерфейса} выполняется до фонового потока
Uma Achanta
похожи на сопрограммы Котлина
user25
40

в Xcode 8 используйте:

DispatchQueue.global(qos: .userInitiated).async { }
Marco
источник
26

Swift 5.2, 4 и позже

Основные и фоновые очереди

let main = DispatchQueue.main
let background = DispatchQueue.global()
let helper = DispatchQueue(label: "another_thread") 

Работа с асинхронными и синхронизирующими потоками!

 background.async { //async tasks here } 
 background.sync { //sync tasks here } 

Асинхронные потоки будут работать вместе с основным потоком.

Синхронизация потоков будет блокировать основной поток во время выполнения.

Saranjith
источник
1
А как бы вы использовали синхронизацию потоков без блокировки основного потока (UI) ?? Я хотел бы выполнить ряд вещей в фоновом режиме - но эти вещи должны выполняться синхронно друг за другом. В течение этого времени пользовательский интерфейс должен оставаться отзывчивым .... Как бы вы это сделали?
икк
Используйте NSOperationQueue. Какая ваша каждая задача представляет NSOperation. см. stackoverflow.com/a/19746890/5215474
Саранджит
12

Swift 4.1 и 5. Мы используем очереди во многих местах нашего кода. Итак, я создал класс Threads со всеми очередями. Если вы не хотите использовать класс Threads, вы можете скопировать нужный код очереди из методов класса.

class Threads {

  static let concurrentQueue = DispatchQueue(label: "AppNameConcurrentQueue", attributes: .concurrent)
  static let serialQueue = DispatchQueue(label: "AppNameSerialQueue")

  // Main Queue
  class func performTaskInMainQueue(task: @escaping ()->()) {
    DispatchQueue.main.async {
      task()
    }
  }

  // Background Queue
  class func performTaskInBackground(task:@escaping () throws -> ()) {
    DispatchQueue.global(qos: .background).async {
      do {
        try task()
      } catch let error as NSError {
        print("error in background thread:\(error.localizedDescription)")
      }
    }
  }

  // Concurrent Queue
  class func perfromTaskInConcurrentQueue(task:@escaping () throws -> ()) {
    concurrentQueue.async {
      do {
        try task()
      } catch let error as NSError {
        print("error in Concurrent Queue:\(error.localizedDescription)")
      }
    }
  }

  // Serial Queue
  class func perfromTaskInSerialQueue(task:@escaping () throws -> ()) {
    serialQueue.async {
      do {
        try task()
      } catch let error as NSError {
        print("error in Serial Queue:\(error.localizedDescription)")
      }
    }
  }

  // Perform task afterDelay
  class func performTaskAfterDealy(_ timeInteval: TimeInterval, _ task:@escaping () -> ()) {
    DispatchQueue.main.asyncAfter(deadline: (.now() + timeInteval)) {
      task()
    }
  }
}

Пример, показывающий использование основной очереди.

override func viewDidLoad() {
    super.viewDidLoad()
     Threads.performTaskInMainQueue {
        //Update UI
    }
}
Гурджиндер Сингх
источник