Як я можу 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")
}

Або будь-яке з усіх інших видів використання Grand Central Dispatch API ...

Тепер, коли я відкрив свій проект у Xcode 8 (бета) для Swift 3, я отримую всілякі помилки. Деякі з них пропонують виправити мій код, але не всі виправлення створюють робочий код. Що я роблю з цього приводу?


Відповіді:


343

З самого початку Swift надав деякі можливості зробити ObjC та C більш Swifty, додаючи більше з кожною версією. Тепер у Swift 3 нова функція "імпортувати як член" дозволяє створювати рамки з певними стилями API API - де у вас є тип даних, який працює як клас, і купа глобальних функцій для роботи з ним - діють більше, як рідні API Swift. Типи даних імпортуються як класи Swift, пов'язані з ними глобальні функції імпортуються як методи та властивості для цих класів, а деякі пов'язані з ними речі, такі як набори констант, можуть стати підтипами, де це доречно.

У бета-версії Xcode 8 / Swift 3, Apple застосувала цю функцію (разом з кількома іншими), щоб зробити рамку Dispatch набагато швидкішою. (І 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для виконання роботи пізніше? Це метод і в чергах, і він займає a DispatchTime, в якому є оператори для різних числових типів, щоб ви могли просто додати цілі або дробові секунди:

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

Ви можете знайти свій шлях до нового API диспетчеризації, відкривши його інтерфейс у Xcode 8 - скористайтеся функцією "Відкрити швидко", щоб знайти модуль диспетчеризації, або DispatchQueueвведіть символ (як ) у ваш проект / майданчик Swift та клацніть по ньому, а потім перейдіть навколо модуль звідти. (Ви можете знайти API Swift Dispatch на новому веб-сайті Apple, довідковому API API та в програмі перегляду документів в Xcode, але схоже, що вміст doc з версії C ще не перемістився до нього.)

Для отримання порад див. Посібник з міграції .


3
Що стосується Xcode 8 Beta 6, то атрибут .serial відсутній і поведінка за замовчуванням - forums.developer.apple.com/message/159457#159457
hyouuu

6
Це потребує оновлення, оскільки XCode 8.1 .. ярлик атрибутів зник, і на його місці ми можемо використовувати "DispatchQueue.global (qos: .background) .async"
Майк М

2
Чудова відповідь. Дійсно допоміг мені обійти голову.
Мохсін Хубайб Ахмед

Мені довелося використовувати qos:замістьattributes:
Іслам В.

Чи не повинно бути це myQueue.async {в class Fooприкладі?
vacawama

142

У Xcode 8 бета-4 не працює ...

Використання:

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

для асинхронізації двома способами:

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

DispatchQueue.main.async( execute: {
    print("Async2")
})

Так що він не блокує інтерфейс користувача?
користувач25

72

Цей хороший приклад для Swift 4приблизно async:

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

привіт DispatchQueue.main.async {// Запустити оновлення інтерфейсу користувача} виконується перед фоновою ниткою
Uma Achanta

схожий на
кроки Котліна


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 } 

Асинхронні потоки працюватимуть разом з основною ниткою.

Нитки синхронізації блокують основний потік під час виконання.


1
І як би ви використовували потоки Sync, не блокуючи основний потік (UI) ?? Я хотів би виконати ряд речей у фоновому режимі, але тези повинні виконуватись одна за одною синхронізовано. Протягом цього часу користувальницький інтерфейс повинен залишатися чуйним .... Як би ви це зробили?
iKK

Використовуйте NSOperationQueue. Яке ваше кожне завдання, що представляє НСО. см stackoverflow.com/a/19746890/5215474
Saranjith

12

Швидкий 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
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.