Как мне написать 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?

brandonscript
источник
6
Дополнительную информацию о процессе миграции можно найти здесь: https://swift.org/migration-guide/. Для этого вопроса
важен
Ваш вопрос должен быть UInt64?
Мед

Ответы:

1126

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

// to run something in 0.1 seconds

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

Обратите внимание, что приведенный выше синтаксис добавления secondsкак a Doubleкажется источником путаницы (особенно если мы привыкли добавлять nsec). Этот Doubleсинтаксис «добавить секунды как » работает, потому что deadlineесть DispatchTimeи, за кадром, есть +оператор, который возьмет Doubleи добавит столько секунд к DispatchTime:

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

Но если вы действительно хотите добавить целое число msec, μs или nsec к DispatchTime, вы также можете добавить a DispatchTimeIntervalк a 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
Ой, извини. Здесь уже слишком поздно :). Мысль, что все беспорядок должен идти на самом деле, но не сделал прыжок. IMO «простое» решение - это «одно истинное решение».
tobiasdm
1
@ Роб, как бы я отменил это? Спасибо.
призрак kemicofa
Итак, как добавить динамическое ожидание? Например, у меня есть номер let: Float = 1.0. И .now () + .milliseconds (число) не работает. Также не Double (число). Я не могу понять это.
Кьелл
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!")
}
rockdaswift
источник
DispatchQueue.main.asyncAfter (deadline:) не работает. Он говорит, что не перегружает никакой метод из своего суперкласса.
Фабрицио Бартоломуччи
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

Несколько иной вкус принятого ответа.

Swift 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

Swift 4

Вы можете создать расширение в DispatchQueue и добавить функцию delay, которая использует 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?
brandonscript
как я уже упоминал, он оборачивает asyncAfter внутри функции executeAfter, которая принимает задержку в качестве параметра, и ее проще вызывать, используя просто executeAfter (delay: 2) {}
Suhit Patil
Параметры закрытия по умолчанию не экранированы, @escaping указывает, что параметр закрытия может экранироваться. добавлен параметр @ escaping в закрытии, чтобы сохранить потенциальный сбой.
Сухит Патил
3

вызов DispatchQueue.main.after(when: DispatchTime, execute: () -> Void)

Я настоятельно рекомендую использовать инструменты XCode для преобразования в Swift 3 («Правка»> «Преобразовать»> «В текущий синтаксис Swift»). Уловил это для меня

jjatie
источник
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
}
IOS
источник
3
Не уверены, чем это отличается от принятого ответа?
brandonscript
3

Swift 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) {
    // ...
}
MANN
источник
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
Не уверены, чем это отличается от принятого ответа?
brandonscript
0

Ты можешь использовать

DispatchQueue.main.asyncAfter(deadline: .now() + .microseconds(100)) {
        // Code
    }
Парт Дхорда
источник
0

попробуй это

let when = DispatchTime.now() + 1.5
    DispatchQueue.main.asyncAfter(deadline: when) {
        //some code
    }
A.Guz
источник
Не уверен, как это отличается от затронутого ответа?
brandonscript