Bagaimana cara saya menulis dispatch_after GCD di Swift 3, 4, dan 5?


445

Di Swift 2, saya bisa menggunakan dispatch_afteruntuk menunda tindakan menggunakan grand central dispatch:

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 
})

Tetapi ini sepertinya tidak lagi dikompilasi sejak Swift 3. Apa cara yang disukai untuk menulis ini dalam Swift modern?


6
Informasi lebih lanjut tentang proses migrasi dapat ditemukan di sini: https://swift.org/migration-guide/ Bagian "Pengiriman" adalah yang relevan untuk pertanyaan ini
tonik12

seharusnya pertanyaan Anda UInt64?
Sayang

Jawaban:


1125

Sintaksnya sederhana:

// to run something in 0.1 seconds

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

Catatan, sintaksis menambahkan di secondsatas Doubletampaknya menjadi sumber kebingungan (terutama karena kita terbiasa menambahkan nsec). DoubleSintaks "tambahkan detik sebagai " berfungsi karena deadlinemerupakan DispatchTimedan, di belakang layar, ada +operator yang akan mengambil Doubledan menambahkan banyak detik ke DispatchTime:

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

Tetapi, jika Anda benar-benar ingin menambahkan bilangan bulat dari msec, μs, atau nsec ke DispatchTime, Anda juga dapat menambahkan a DispatchTimeIntervalke a DispatchTime. Itu berarti Anda dapat melakukan:

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")
}

Ini semua berjalan mulus karena metode kelebihan beban yang terpisah ini untuk +operator di DispatchTimekelas.

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

Ditanya bagaimana seseorang membatalkan tugas yang dikirim. Untuk melakukan ini, gunakan DispatchWorkItem. Misalnya, ini memulai tugas yang akan diaktifkan dalam lima detik, atau jika pengontrol tampilan diberhentikan dan tidak dialokasikan, tugasnya deinitakan dibatalkan:

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() { ... }

}

Perhatikan penggunaan [weak self]daftar tangkap di DispatchWorkItem. Ini penting untuk menghindari siklus referensi yang kuat. Perhatikan juga bahwa ini tidak melakukan pembatalan preemptive, tetapi hanya menghentikan tugas dari memulai jika belum. Tetapi jika sudah dimulai saat bertemu dengan cancel()panggilan, blok akan menyelesaikan eksekusi (kecuali jika Anda secara manual memeriksa isCancelleddi dalam blok).


5
Terima kasih telah menunjukkan hal itu, dan bahkan swift.org/migration-guide menyebutkan perlunya melakukan perubahan itu dengan tangan.
matt

1
Oh maaf. Sudah terlambat di sini :). Kupikir semua kekacauan seharusnya terjadi, tetapi tidak mengambil lompatan. IMO solusi "sederhana" adalah solusi yang benar.
tobiasdm

1
@Rob, bagaimana saya membatalkannya? Terima kasih.
hantu kemicofa

Ok jadi bagaimana Anda menambahkan tunggu dinamis? Sebagai contoh, saya memiliki nomor let: Float = 1.0. Dan .Sekarang () +. Milidetik (angka) tidak berfungsi. Juga tidak Double (angka). Saya tidak bisa mengetahuinya.
Kjell

2
The DispatchTimeIntervalpenafsiran, seperti .millisecondsmembutuhkan Int. Tetapi jika hanya menambahkan detik, saya akan menggunakan Double, misalnya let n: Double = 1.0; queue.asyncAfter(deadline: .now() + n) { ... }.
Rob

128

Swift 4:

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

Untuk saat ini .seconds(Int), .microseconds(Int)dan .nanoseconds(Int)dapat juga digunakan.


7
.millisecondslebih baik daripada Double.
DawnSong

5
Sangat bagus. Catatan untuk orang lain: Anda juga dapat menggunakan DispatchTimeIntervalnilai enum lainnya . case seconds(Int) case milliseconds(Int) case microseconds(Int) case nanoseconds(Int)
Rob MacEachern

@RobMacEachern, terima kasih saran yang bagus saya tambahkan ke jawabannya.
Sverrisson

2
.milliseconds is better than Double. - Saya ingin itu memakai T-shirt;).
Chris Prince

58

Jika Anda hanya ingin fungsi penundaan di

Swift 4 & 5

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

Anda bisa menggunakannya seperti:

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

DispatchQueue.main.asyncAfter (batas waktu:) tidak berfungsi. Ia mengatakan tidak membebani metode apa pun dari superclass-nya.
Fabrizio Bartolomucci

7
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1, execute: closure)lebih sederhana.
DawnSong

16

setelah rilis Swift 3, @escaping juga harus ditambahkan

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

5

Rasa yang agak berbeda dari Jawaban yang Diterima.

Cepat 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

Cepat 4

Anda dapat membuat ekstensi di DispatchQueue dan menambahkan penundaan fungsi yang menggunakan DispatchQueuefungsi asyncAfter secara internal

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

dan gunakan

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

2
Apa bedanya dengan jawaban @ rockdaswift?
naskah merek

seperti yang saya sebutkan itu membungkus asyncAfter di dalam performAfter fungsi yang mengambil delay sebagai parameter dan dapat lebih mudah untuk memanggil menggunakan performAfter (delay: 2) {}
Suhit Patil

Parameter penutupan adalah non-escaping secara default, @escaping menunjukkan bahwa parameter penutupan dapat keluar. menambahkan @ escaping parameter pada penutupan untuk menghemat potensi kerusakan.
Suhit Patil

3

panggilan DispatchQueue.main.after(when: DispatchTime, execute: () -> Void)

Saya sangat merekomendasikan menggunakan alat Xcode untuk mengkonversi ke Swift 3 (Edit> Convert> To Current Swift Syntax). Ini menangkap ini untukku


3

Dalam Swift 4.1 dan Xcode 9.4.1

Jawaban sederhana adalah ...

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

3
Tidak yakin bagaimana ini berbeda dari jawaban yang diterima?
naskah

3

Cepat 5 dan lebih tinggi

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

1

Tidak ada jawaban yang disebutkan berjalan di utas non-utama, jadi tambahkan 2 sen saya.

Di antrian utama (utas utama)

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

ATAU

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

Pada antrian global (utas bukan utama, berdasarkan QOS yang ditentukan).

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

ATAU

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

0

Ini bekerja untuk saya di 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
Tidak yakin bagaimana ini berbeda dari jawaban yang diterima?
naskah merek

0

Kamu bisa menggunakan

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

0

coba ini

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

Tidak yakin bagaimana ini berbeda dari jawaban yang terpengaruh?
Naskah Naskah
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.