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

6 ответов

Решение

С самого начала 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?")
}

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

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

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

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

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

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

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

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 } 

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

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

Свифт 4.1. Мы используем очереди во многих местах в нашем коде. Итак, я создал класс 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 {
        self.tblViewSignUP.reloadData()
    }
}
Другие вопросы по тегам