Як я можу 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.