Как я могу отправить 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()
}
}