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?

naskah merek
sumber
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).

rampok
sumber
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.

Sverrisson
sumber
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!")
}
rockdaswift
sumber
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()
  }
}
Marco Pappalardo
sumber
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)")
 }
Md. Ibrahim Hassan
sumber
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")
}
Suhit Patil
sumber
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

jjatie
sumber
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
}
iOS
sumber
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                 
})
midhun p
sumber
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) {
    // ...
}
BANYAK
sumber
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)")
}
Ankit garg
sumber
5
Tidak yakin bagaimana ini berbeda dari jawaban yang diterima?
naskah merek
0

Kamu bisa menggunakan

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

coba ini

let when = DispatchTime.now() + 1.5
    DispatchQueue.main.asyncAfter(deadline: when) {
        //some code
    }
A.Guz
sumber
Tidak yakin bagaimana ini berbeda dari jawaban yang terpengaruh?
Naskah Naskah