Как отправить dispatch_sync, dispatch_async, dispatch_after и т.д. В Swift 3, Swift 4 и дальше?

У меня есть много кода в проектах 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, я получаю всевозможные ошибки. Некоторые из них предлагают исправить мой код, но не все исправления создают рабочий код. Что мне делать с этим?

Ответ 1

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

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

Первым шагом при перемещении любого проекта в Swift 3 должно быть открытие его в Xcode 8 и в меню выберите "Редактирование" > "Преобразовать" > "Текущий синтаксис Swift".... Это применит (с вашим обзором и одобрением) все изменения, необходимые для всех переименованных 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/playground и командную строку, щелкните по нему, а затем разглядывайте модуль. (Вы можете найти API Swift Dispatch в новом веб-сайте API Reference и в приложении Xcode doc, но это похоже на содержимое документа из версии C еще не переместился в нее.)

Подробнее см. в Руководство по миграции.

Ответ 2

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

Ответ 3

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

DispatchQueue.global(qos: .background).async {
    // Background Thread
    DispatchQueue.main.async {
        // Run UI Updates or call completion block
    }
}

Ответ 4

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

DispatchQueue.global(qos: .userInitiated).async { }

Ответ 5

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

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

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

Ответ 6

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