Як записати dispatch_after GCD у Swift 3, 4 та 5?


445

У Swift 2 мені вдалося dispatch_afterзатримати дію, використовуючи грандіозну центральну розсилку:

var dispatchTime: dispatch_time_t = dispatch_time(DISPATCH_TIME_NOW, Int64(0.1 * Double(NSEC_PER_SEC))) 
dispatch_after(dispatchTime, dispatch_get_main_queue(), { 
    // your function here 
})

Але це більше не здається компілювати з Swift 3. Який найкращий спосіб написати це в сучасному Swift?


6
Додаткову інформацію про процес міграції можна знайти тут: https://swift.org/migration-guide/ Розділ "Відправка" є відповідним для цього питання
tonik12

чи має бути ваше запитання UInt64?
Мед

Відповіді:


1125

Синтаксис просто:

// to run something in 0.1 seconds

DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
    // your code here
}

Зауважимо, вищевказаний синтаксис додавання secondsяк Doubleздається є джерелом плутанини (особливо, оскільки ми звикли додавати nsec). Цей Doubleсинтаксис "додати секунди як " працює тому, що deadlineє, DispatchTimeа за лаштунками є +оператор, який займе a Doubleі додасть стільки секунд до DispatchTime:

public func +(time: DispatchTime, seconds: Double) -> DispatchTime

Але якщо ви дійсно хочете додати до числа ціле число msec, μs або nsec DispatchTime, ви також можете додати DispatchTimeIntervala DispatchTime. Це означає, що ви можете:

DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(500)) {
    os_log("500 msec seconds later")
}

DispatchQueue.main.asyncAfter(deadline: .now() + .microseconds(1_000_000)) {
    os_log("1m μs seconds later")
}

DispatchQueue.main.asyncAfter(deadline: .now() + .nanoseconds(1_500_000_000)) {
    os_log("1.5b nsec seconds later")
}

Усі вони безперебійно працюють через цей окремий метод перевантаження для +оператора в DispatchTimeкласі.

public func +(time: DispatchTime, interval: DispatchTimeInterval) -> DispatchTime

Було запитано, як можна скасувати відправлене завдання. Для цього використовуйте DispatchWorkItem. Наприклад, це запускає завдання, яке запуститься через п’ять секунд, або якщо контролер перегляду буде відпущений та розміщений, deinitвін скасує завдання:

class ViewController: UIViewController {

    private var item: DispatchWorkItem?

    override func viewDidLoad() {
        super.viewDidLoad()

        item = DispatchWorkItem { [weak self] in
            self?.doSomething()
            self?.item = nil
        }

        DispatchQueue.main.asyncAfter(deadline: .now() + 5, execute: item!)
    }

    deinit {
        item?.cancel()
    }

    func doSomething() { ... }

}

Зверніть увагу на використання списку [weak self]захоплення у DispatchWorkItem. Це важливо, щоб уникнути сильного еталонного циклу. Також зауважте, що це не робить превентивне скасування, а просто зупиняє роботу, якщо вона ще не почалася. Але якщо він вже розпочався до моменту, коли він стикається з cancel()викликом, блок закінчить його виконання (якщо ви не вручну перевіряєте isCancelledвсередині блоку).


5
Дякуємо, що вказали на це, і насправді swift.org/migration-guide згадує про необхідність зробити цю зміну вручну.
мат

1
Ой, вибачте. Тут вже занадто пізно :). Думав, що весь безлад повинен піти насправді, але не скочив. ІМО "просте" рішення - це єдине істинне рішення.
tobiasdm

1
@Rob, як би я погодився скасувати його? Дякую.
kemicofa ghost

Гаразд, як додати динамічне очікування? Наприклад, у мене є номер дозволу: Float = 1.0. І .now () + .milliseconds (число) не працює. Також не робить Double (число). Я не можу це зрозуміти.
Kjell

2
У DispatchTimeIntervalвидач, як .millisecondsпотрібно Int. Але якби просто додавати секунди, я б використав Double, напр let n: Double = 1.0; queue.asyncAfter(deadline: .now() + n) { ... }.
Роб

128

Швидкий 4:

DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(100)) {
   // Code
}

На час .seconds(Int), .microseconds(Int)а .nanoseconds(Int)також може бути використаний.


7
.millisecondsкраще, ніж Double.
DawnSong

5
Дуже хороший. Примітка для інших: ви також можете використовувати будь-яке інше DispatchTimeIntervalзначення перерахунків. case seconds(Int) case milliseconds(Int) case microseconds(Int) case nanoseconds(Int)
Роб МакЕчерн

@RobMacEachern, дякую хорошій пропозиції, я додаю це у відповідь.
Сверріссон

2
.milliseconds is better than Double. - Я хочу, щоб це було на футболці;).
Кріс Принс

58

Якщо ви просто хочете функцію затримки в

Швидкий 4 і 5

func delay(interval: TimeInterval, closure: @escaping () -> Void) {
     DispatchQueue.main.asyncAfter(deadline: .now() + interval) {
          closure()
     }
}

Ви можете використовувати його так:

delay(interval: 1) { 
    print("Hi!")
}

DispatchQueue.main.asyncAfter (термін:) не працює. Він говорить, що не перевантажує жоден метод зі свого суперкласу.
Фабріціо Бартоломуччі

7
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1, execute: closure)простіше.
DawnSong

16

після виходу Swift 3 також потрібно додати @escaping

func delay(_ delay: Double, closure: @escaping () -> ()) {
  DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
    closure()
  }
}

5

Дещо інший аромат прийнятого відповіді.

Швидкий 4

DispatchQueue.main.asyncAfter(deadline: .now() + 0.1 + .milliseconds(500) + 
.microseconds(500) + .nanoseconds(1000)) {
                print("Delayed by 0.1 second + 500 milliseconds + 500 microseconds + 
                      1000 nanoseconds)")
 }

5

Швидкий 4

Ви можете створити розширення на DispatchQueue і додати функцію затримки, яка використовує DispatchQueueфункцію asyncAfter внутрішньо

extension DispatchQueue {
   static func delay(_ delay: DispatchTimeInterval, closure: @escaping () -> ()) {
      DispatchQueue.main.asyncAfter(deadline: .now() + delay, execute: closure)
   }
}

і використовувати

DispatchQueue.delay(.milliseconds(10)) {
   print("task to be done")
}

2
Чим це відрізняється від відповіді @ rockdaswift?
фірмовий сценарій

як я вже згадував, він завершує функцію asyncAfter всередині функції performAfter, яка приймає затримку як параметр, і його можна буде простіше викликати, використовуючи функцію just performAfter (затримка: 2) {}
Suhit Patil

Параметри закриття за замовчуванням не виходять, а @escaping вказує на те, що параметр закриття може вийти. додано @ параметр уникнення закриття для збереження потенційної аварії.
Suhit Patil

3

дзвінок DispatchQueue.main.after(when: DispatchTime, execute: () -> Void)

Я настійно рекомендую використовувати інструменти Xcode для переходу до Swift 3 (Edit> Convert> To Current Swift Syntax). Це мене наловило


3

У Swift 4.1 та Xcode 9.4.1

Проста відповідь ...

//To call function after 5 seconds time
DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
//Here call your function
}

3
Не впевнені, чим це відрізняється від прийнятої відповіді?
фірмовий сценарій

3

Швидкість 5 і вище

DispatchQueue.main.asyncAfter(deadline: .now() + 2, execute: {
   // code to execute                 
})

1

Жодна з вказаних відповідей не працює на головній темі, тому додаю мої 2 центи.

На головній черзі (основна тема)

let mainQueue = DispatchQueue.main
let deadline = DispatchTime.now() + .seconds(10)
mainQueue.asyncAfter(deadline: deadline) {
    // ...
}

АБО

DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + .seconds(10)) { 
    // ...
}

У глобальній черзі (не основний потік, заснований на заданій QOS).

let backgroundQueue = DispatchQueue.global()
let deadline = DispatchTime.now() + .milliseconds(100)
backgroundQueue.asyncAfter(deadline: deadline, qos: .background) { 
    // ...
}

АБО

DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + .milliseconds(100), qos: .background) {
    // ...
}

0

Це працювало для мене у Swift 3

let time1 = 8.23
let time2 = 3.42

// Delay 2 seconds


DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
    print("Sum of times: \(time1 + time2)")
}

5
Не впевнені, чим це відрізняється від прийнятої відповіді?
фірмовий сценарій


0

спробуйте це

let when = DispatchTime.now() + 1.5
    DispatchQueue.main.asyncAfter(deadline: when) {
        //some code
    }

Не впевнені, чим це відрізняється від відповіді, про яку йдеться?
фірмовий сценарій
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.