dispatch_after - GCD di Swift?


558

Saya sudah membaca iBook dari Apple, dan tidak dapat menemukan definisi tentang itu:

Bisakah seseorang menjelaskan struktur dispatch_after?

dispatch_after(<#when: dispatch_time_t#>, <#queue: dispatch_queue_t?#>, <#block: dispatch_block_t?#>)

1
Apple tidak menerbitkan buku ini pada 2018. Arsip terbaru yang bisa saya temukan adalah mulai Desember 2017 . Tautan lama ke iBook sekarang cukup redirect ke developer.apple.com/documentation/swift .
Cœur

Jawaban:


742

Ide struktur yang lebih jelas:

dispatch_after(when: dispatch_time_t, queue: dispatch_queue_t, block: dispatch_block_t?)

dispatch_time_tadalah a UInt64. Ini dispatch_queue_tsebenarnya adalah jenis alias NSObject, tetapi Anda harus menggunakan metode GCD yang Anda kenal untuk mendapatkan antrian. Blok adalah penutupan Swift. Secara khusus, dispatch_block_tdidefinisikan sebagai () -> Void, yang setara dengan () -> ().

Contoh penggunaan:

let delayTime = dispatch_time(DISPATCH_TIME_NOW, Int64(1 * Double(NSEC_PER_SEC)))
dispatch_after(delayTime, dispatch_get_main_queue()) {
    print("test")
}

EDIT:

Saya sarankan menggunakan fungsi @ matt yang sangat bagusdelay .

EDIT 2:

Di Swift 3, akan ada pembungkus baru untuk GCD. Lihat di sini: https://github.com/apple/swift-evolution/blob/master/proposals/0088-libdispatch-for-swift3.md

Contoh asli akan ditulis sebagai berikut dalam Swift 3:

let deadlineTime = DispatchTime.now() + .seconds(1)
DispatchQueue.main.asyncAfter(deadline: deadlineTime) {
    print("test")
}

Perhatikan bahwa Anda dapat menulis deadlineTimedeklarasi sebagai DispatchTime.now() + 1.0dan mendapatkan hasil yang sama karena +operator ditimpa sebagai berikut (sama untuk -):

  • func +(time: DispatchTime, seconds: Double) -> DispatchTime
  • func +(time: DispatchWalltime, interval: DispatchTimeInterval) -> DispatchWalltime

Ini berarti bahwa jika Anda tidak menggunakan DispatchTimeInterval enumdan hanya menulis angka, diasumsikan bahwa Anda menggunakan detik.


17
Kiat: Karena blok adalah parameter terakhir dari fungsi, Anda dapat menggunakan sintaks "trailing closure" Swift untuk keterbacaan ekstra:dispatch_after(1, dispatch_get_main_queue()) { println("test") }
Bill

8
Saya pikir menggunakan nomor 1dalam dispatch_after(1, ...dapat menyebabkan banyak kebingungan di sini. Orang akan berpikir itu beberapa detik, padahal sebenarnya nano-detik . Saya sarankan lihat jawaban @ brindy tentang cara membuat nomor ini dengan benar.
Hlung

3
Harap ubah 1ke dispatch_time(DISPATCH_TIME_NOW, Int64(1 * Double(NSEC_PER_SEC)))karena menyebabkan kebingungan. Orang-orang dapat berpikir bahwa Anda tidak perlu membuat dispatch_time_t di Swift
OemerA

4
Versi Swift 3 tampaknya tidak berfungsi. Ini keluhan bahwa Binary operator '+' cannot be applied to operands of type DispatchTime and '_'di teleponlet delayTime = DispatchTime.now() + .seconds(1.0)
Andy Ibanez

9
Menulis ulang DispatchTime.now() + 1.0sepertinya menjadi satu-satunya cara untuk membuatnya bekerja (tidak perlu .seconds)
Andy Ibanez

1093

Saya dispatch_aftersering menggunakan ini sehingga saya menulis fungsi utilitas tingkat atas untuk membuat sintaks lebih sederhana:

func delay(delay:Double, closure:()->()) {
    dispatch_after(
        dispatch_time(
            DISPATCH_TIME_NOW,
            Int64(delay * Double(NSEC_PER_SEC))
        ),
        dispatch_get_main_queue(), closure)
}

Dan sekarang Anda dapat berbicara seperti ini:

delay(0.4) {
    // do stuff
}

Wow, bahasa tempat Anda bisa meningkatkan bahasa. Apa yang bisa lebih baik?


Pembaruan untuk Swift 3, Xcode 8 Seed 6

Tampaknya hampir tidak layak untuk diganggu, sekarang mereka telah meningkatkan sintaks pemanggilan:

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

2
Saya hanya perlu pintasan untuk perhitungan keterlambatan, berakhir dengan:func delayInSec(delay: Double) -> dispatch_time_t { return dispatch_time(DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC))) }
Aviel Gross

4
@ agf119105 Jika Anda hanya memiliki satu baris kode di penutup, tambahkan satu baris kode lainnya (mis return.).
matt

2
@GastonM Tidak relevan. Melewati fungsi memiliki sendiri tidak ada masalah manajemen memori.
matt

7
"Bahasa tempat Anda dapat meningkatkan bahasa". Saya tidak mengerti bagaimana mendefinisikan fungsi global meningkatkan bahasa, atau mengapa ini tidak bisa dilakukan di C bahkan. Mungkin jika Anda membebani operator;)1.0 ~~ { code...}
Yerk

8
Tidak mempertanyakan kebenaran jawaban Anda — tetapi bukankah "Saya sering menggunakan dispatch_after" sebuah aroma kode yang sebaiknya diperjuangkan dengan tidak menyediakan fungsi kenyamanan?
Nikolai Ruhe

128

Swift 3+

Ini sangat mudah dan elegan di Swift 3+:

DispatchQueue.main.asyncAfter(deadline: .now() + 4.5) {
    // ...
}

Jawaban Lama:

Untuk memperluas jawaban Cezary, yang akan dieksekusi setelah 1 nanosecond, saya harus melakukan yang berikut untuk mengeksekusi setelah 4 setengah detik.

let delay = 4.5 * Double(NSEC_PER_SEC)
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))
dispatch_after(time, dispatch_get_main_queue(), block)

Sunting: Saya menemukan bahwa kode asli saya sedikit salah. Mengetik tersirat menyebabkan kesalahan kompilasi jika Anda tidak melemparkan NSEC_PER_SEC ke Dobel.

Kalau ada yang bisa menyarankan solusi yang lebih optimal saya akan senang mendengarnya.


Saya mendapatkan kesalahan kompilator dengan API yang tidak digunakan lagi dispatch_get_current_queue(). Saya menggunakan dispatch_get_main_queue()sebagai gantinya.
David L

@ DavidL - terima kasih, dispatch_get_main_queue()sudah pasti apa yang harus Anda gunakan. Akan diperbarui.
brindy

saya mencoba ini di taman bermain dengan cepat 3 dan tidak berhasil
μολὼν.λαβέ

@GAlexander Bekerja untuk saya. Apakah Anda mengizinkan taman bermain untuk dieksekusi tanpa batas?
brindy

uhm, baik tidak, saya membiarkan menjalankan lari selama beberapa jam dan masih tidak ada yang dicetak. inilah yang saya gunakan. "import Dispatch import Darwin import CoreGraphics 'DispatchQueue.main.asyncSetelah (batas waktu: .sekarang () + 4,5) {print (" got here ")}"
μολὼν.λαβέ

83

Sintaks matt sangat bagus dan jika Anda perlu membatalkan pemblokiran, Anda mungkin ingin menggunakan ini:

typealias dispatch_cancelable_closure = (cancel : Bool) -> Void

func delay(time:NSTimeInterval, closure:()->Void) ->  dispatch_cancelable_closure? {

    func dispatch_later(clsr:()->Void) {
        dispatch_after(
            dispatch_time(
                DISPATCH_TIME_NOW,
                Int64(time * Double(NSEC_PER_SEC))
            ),
            dispatch_get_main_queue(), clsr)
    }

    var closure:dispatch_block_t? = closure
    var cancelableClosure:dispatch_cancelable_closure?

    let delayedClosure:dispatch_cancelable_closure = { cancel in
        if closure != nil {
            if (cancel == false) {
                dispatch_async(dispatch_get_main_queue(), closure!);
            }
        }
        closure = nil
        cancelableClosure = nil
    }

    cancelableClosure = delayedClosure

    dispatch_later {
        if let delayedClosure = cancelableClosure {
            delayedClosure(cancel: false)
        }
    }

    return cancelableClosure;
}

func cancel_delay(closure:dispatch_cancelable_closure?) {

    if closure != nil {
        closure!(cancel: true)
    }
}

Gunakan sebagai berikut

let retVal = delay(2.0) {
    println("Later")
}
delay(1.0) {
    cancel_delay(retVal)
}

kredit

Tautan di atas tampaknya turun. Kode Objc asli dari Github


1
Fitur kinerja yang memiliki PerformSelector: afterDelay adalah kemampuan untuk membatalkannya. Hanya solusi ini yang mengatasi masalah. Terima kasih
HotJard

@HotJard Note yang performSelector:afterDelay:sekarang tersedia di Swift 2, jadi Anda bisa membatalkannya.
matt

@ Matt tetapi hanya tersedia untuk NSObject, bukan?
HotJard

@ HotJard Tentu tapi itu lebih baik daripada tidak memilikinya sama sekali. Saya tidak melihat masalah di sana. Namun, seperti halnya dengan jawaban ini, saya sudah mengganti kerugiannya dengan menulis timer yang dapat dibatalkan berbasis GCD (menggunakan a dispatch_source_t, karena itu adalah sesuatu yang dapat Anda batalkan).
matt

2
Terima kasih banyak, saya telah menggunakan ini hingga Swift 2.3. Kompiler Swift 3.0 mengeluh sekarang, akan lebih bagus jika Anda memperbarui jawaban Anda!
otomatis

27

Solusi paling sederhana di Swift 3.0 & Swift 4.0 & Swift 5.0

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

Pemakaian

delayWithSeconds(1) {
   //Do something
}

22

Apple memiliki cuplikan dispatch_after untuk Objective-C :

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(<#delayInSeconds#> * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    <#code to be executed after a specified delay#>
});

Berikut ini cuplikan yang sama yang porting ke Swift 3:

DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + <#delayInSeconds#>) {
  <#code to be executed after a specified delay#>
}

14

Cara lain adalah dengan memperluas Dobel seperti ini:

extension Double {
   var dispatchTime: dispatch_time_t {
       get {
           return dispatch_time(DISPATCH_TIME_NOW,Int64(self * Double(NSEC_PER_SEC)))
       }
   }
}

Maka Anda dapat menggunakannya seperti ini:

dispatch_after(Double(2.0).dispatchTime, dispatch_get_main_queue(), { () -> Void in
            self.dismissViewControllerAnimated(true, completion: nil)
    })

Saya suka fungsi penundaan matt tetapi hanya karena preferensi saya lebih suka membatasi penutupan lewat sekitar.


8

Di Swift 3.0

Antrian pengiriman

  DispatchQueue(label: "test").async {
        //long running Background Task
        for obj in 0...1000 {
            print("async \(obj)")
        }

        // UI update in main queue
        DispatchQueue.main.async(execute: { 
            print("UI update on main queue")
        })

    }

    DispatchQueue(label: "m").sync {
        //long running Background Task
        for obj in 0...1000 {
            print("sync \(obj)")
        }

        // UI update in main queue
        DispatchQueue.main.sync(execute: {
            print("UI update on main queue")
        })
    }

Pengiriman setelah 5 detik

    DispatchQueue.main.after(when: DispatchTime.now() + 5) {
        print("Dispatch after 5 sec")
    }

4

Versi Swift 3.0

Fungsi penutupan berikut menjalankan beberapa tugas setelah penundaan pada utas utama.

func performAfterDelay(delay : Double, onCompletion: @escaping() -> Void){

    DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + delay, execute: {
       onCompletion()
    })
}

Sebut fungsi ini seperti:

performAfterDelay(delay: 4.0) {
  print("test")
}

4

1) Tambahkan metode ini sebagai bagian dari Ekstensi UIViewController.

extension UIViewController{
func runAfterDelay(delay: NSTimeInterval, block: dispatch_block_t) {
        let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC)))
        dispatch_after(time, dispatch_get_main_queue(), block)
    }
}

Panggil metode ini di VC:

    self.runAfterDelay(5.0, block: {
     //Add code to this block
        print("run After Delay Success")
    })

2)

performSelector("yourMethod Name", withObject: nil, afterDelay: 1)

3)

override func viewWillAppear(animated: Bool) {

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2), dispatch_get_main_queue(), { () -> () in
    //Code Here
})

// Formulir Ringkas

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2), dispatch_get_main_queue()) {
    //Code here
 }
}

3

Meskipun bukan pertanyaan asli oleh OP, beberapa NSTimerpertanyaan terkait telah ditandai sebagai duplikat dari pertanyaan ini, jadi ada baiknya menyertakan NSTimerjawaban di sini.

NSTimer vs. dispatch_after

  • NSTimerlebih tinggi sementara dispatch_afterlebih rendah.
  • NSTimerlebih mudah dibatalkan. Membatalkan dispatch_aftermembutuhkan penulisan lebih banyak kode .

Menunda tugas dengan NSTimer

Buat sebuah NSTimerinstance.

var timer = NSTimer()

Mulai timer dengan penundaan yang Anda butuhkan.

// invalidate the timer if there is any chance that it could have been called before
timer.invalidate()
// delay of 2 seconds
timer = NSTimer.scheduledTimerWithTimeInterval(2.0, target: self, selector: #selector(delayedAction), userInfo: nil, repeats: false) 

Tambahkan fungsi yang akan dipanggil setelah penundaan (menggunakan nama apa pun yang Anda gunakan untuk selectorparameter di atas).

func delayedAction() {
    print("Delayed action has now started."
}

Catatan

  • Jika Anda perlu membatalkan tindakan sebelum itu terjadi, cukup hubungi timer.invalidate().
  • Untuk tindakan berulang, gunakan repeats: true.
  • Jika Anda memiliki acara satu kali tanpa perlu membatalkan maka tidak perlu membuat timervariabel instan. Berikut ini sudah cukup:

    NSTimer.scheduledTimerWithTimeInterval(2.0, target: self, selector: #selector(delayedAction), userInfo: nil, repeats: false) 
  • Lihat jawaban lengkap saya di sini .


3

Untuk beberapa fungsi gunakan ini. Ini sangat membantu untuk menggunakan animasi atau pemuat aktivitas untuk fungsi statis atau Pembaruan UI apa pun.

DispatchQueue.main.asyncAfter(deadline: .now() + 0.9) {
            // Call your function 1
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                // Call your function 2
            }
        }

Misalnya - Gunakan animasi sebelum tableView memuat ulang. Atau pembaruan UI lainnya setelah animasi.

*// Start your amination* 
self.startAnimation()
DispatchQueue.main.asyncAfter(deadline: .now() + 0.9) {
                *// The animation will execute depending on the delay time*
                self.stopAnimation()
                DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                    *// Now update your view*
                     self.fetchData()
                     self.updateUI()
                }
            }

2

Ini berhasil untuk saya.

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

Tujuan-C:

CGFloat time1 = 3.49;
CGFloat time2 = 8.13;

// Delay 2 seconds

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    CGFloat newTime = time1 + time2;
    NSLog(@"New time: %f", newTime);
});

2

Swift 3 & 4:

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

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

menggunakan:

DispatchQueue.delay(.seconds(1)) {
    print("This is after delay")
}

1

Pembantu lain untuk menunda kode Anda yang 100% cepat digunakan dan secara opsional memungkinkan untuk memilih utas yang berbeda untuk menjalankan kode Anda yang tertunda dari:

public func delay(bySeconds seconds: Double, dispatchLevel: DispatchLevel = .main, closure: @escaping () -> Void) {
    let dispatchTime = DispatchTime.now() + seconds
    dispatchLevel.dispatchQueue.asyncAfter(deadline: dispatchTime, execute: closure)
}

public enum DispatchLevel {
    case main, userInteractive, userInitiated, utility, background
    var dispatchQueue: DispatchQueue {
        switch self {
        case .main:                 return DispatchQueue.main
        case .userInteractive:      return DispatchQueue.global(qos: .userInteractive)
        case .userInitiated:        return DispatchQueue.global(qos: .userInitiated)
        case .utility:              return DispatchQueue.global(qos: .utility)
        case .background:           return DispatchQueue.global(qos: .background)
        }
    }
}

Sekarang Anda cukup menunda kode Anda di utas utama seperti ini:

delay(bySeconds: 1.5) { 
    // delayed code
}

Jika Anda ingin menunda kode Anda ke utas berbeda :

delay(bySeconds: 1.5, dispatchLevel: .background) { 
    // delayed code that will run on background thread
}

Jika Anda lebih suka Kerangka yang juga memiliki beberapa fitur yang lebih berguna maka checkout HandySwift . Anda dapat menambahkannya ke proyek Anda melalui Carthage lalu menggunakannya persis seperti pada contoh di atas, misalnya:

import HandySwift    

delay(bySeconds: 1.5) { 
    // delayed code
}

1

Saya selalu lebih suka menggunakan ekstensi daripada fungsi gratis.

Cepat 4

public extension DispatchQueue {

  private class func delay(delay: TimeInterval, closure: @escaping () -> Void) {
    let when = DispatchTime.now() + delay
    DispatchQueue.main.asyncAfter(deadline: when, execute: closure)
  }

  class func performAction(after seconds: TimeInterval, callBack: @escaping (() -> Void) ) {
    DispatchQueue.delay(delay: seconds) {
      callBack()
    }
  }

}

Gunakan sebagai berikut.

DispatchQueue.performAction(after: 0.3) {
  // Code Here
}

1

Menunda panggilan GCD menggunakan asyncAfter in swift

let delayQueue = DispatchQueue(label: "com.theappmaker.in", qos: .userInitiated)
let additionalTime: DispatchTimeInterval = .seconds(2)

Kita dapat menunda sebagai ** mikrodetik , milidetik , nanodetik

delayQueue.asyncAfter(deadline: .now() + 0.60) {
    print(Date())
}

delayQueue.asyncAfter(deadline: .now() + additionalTime) {
    print(Date())
}

1

Dalam Swift 4

Gunakan cuplikan ini:

    let delayInSec = 1.0
    DispatchQueue.main.asyncAfter(deadline: .now() + delayInSec) {
       // code here
       print("It works")
    }

Ini sudah ada di jawaban lain (brindy, misalnya, atau Rahul) ... sintaks yang sama ...
Eric Aya

1
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    // ...
});

The dispatch_after(_:_:_:)Fungsi mengambil tiga parameter:

penundaan
pengiriman antrian
blok atau penutupan

The dispatch_after(_:_:_:)fungsi memanggil blok atau penutupan pada antrian pengiriman yang dilewatkan ke fungsi setelah penundaan diberikan. Perhatikan bahwa penundaan dibuat menggunakandispatch_time(_:_:) fungsi. Ingat ini karena kami juga menggunakan fungsi ini di Swift.

Saya sarankan untuk pergi melalui tutorial tutorial Pengiriman Raywenderlich


1

Di Swift 5, gunakan di bawah ini:

 DispatchQueue.main.asyncAfter(deadline: .now() + 0.2, execute: closure) 

// time gap, specify unit is second
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(2)) {
            Singleton.shared().printDate()
        }
// default time gap is second, you can reduce it
    DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
          // just do it!
    }

0

gunakan kode ini untuk melakukan beberapa tugas terkait UI setelah 2,0 detik.

            let delay = 2.0
            let delayInNanoSeconds = dispatch_time(DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC)))
            let mainQueue = dispatch_get_main_queue()

            dispatch_after(delayInNanoSeconds, mainQueue, {

                print("Some UI related task after delay")
            })

Versi Swift 3.0

Fungsi penutupan berikut menjalankan beberapa tugas setelah penundaan pada utas utama.

func performAfterDelay(delay : Double, onCompletion: @escaping() -> Void){

    DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + delay, execute: {
       onCompletion()
    })
}

Sebut fungsi ini seperti:

performAfterDelay(delay: 4.0) {
  print("test")
}

1
Ini hampir identik dengan jawaban sebelumnya
Daniel Galasko

Tampaknya jawaban ini dilakukan awal 2016, dan lebih tua dari setidaknya 6 jawaban lainnya ..
eharo2

0

Sekarang lebih dari sekadar gula sintaksis untuk kiriman asinkron di Grand Central Dispatch (GCD) di Swift.

tambahkan Podfile

pod 'AsyncSwift'

Lalu, Anda bisa menggunakannya seperti ini.

let seconds = 3.0
Async.main(after: seconds) {
print("Is called after 3 seconds")
}.background(after: 6.0) {
print("At least 3.0 seconds after previous block, and 6.0 after Async code is called")
}

Apple telah memberi kita semua yang diperlukan untuk menggunakan GCD dalam beberapa baris. Kenapa repot dengan pod, ruang kerja dan sebagainya? Cukup baca dokumen tentang @escaping dan capture. cukup.
ingconti

0

Swift 4 memiliki cara yang cukup singkat untuk melakukan ini:

Timer.scheduledTimer(withTimeInterval: 2, repeats: false) { (timer) in
    // Your stuff here
    print("hello")
}

0

Berikut ini adalah versi sinkron dari asyncAfter di Swift:

let deadline = DispatchTime.now() + .seconds(3)
let semaphore = DispatchSemaphore.init(value: 0)
DispatchQueue.global().asyncAfter(deadline: deadline) {
    dispatchPrecondition(condition: .onQueue(DispatchQueue.global()))
    semaphore.signal()
}

semaphore.wait()

Bersama dengan asinkron:

let deadline = DispatchTime.now() + .seconds(3)
DispatchQueue.main.asyncAfter(deadline: deadline) {
    dispatchPrecondition(condition: .onQueue(DispatchQueue.global()))
}
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.