dispatch_after - Swift'de GCD?


558

Apple'dan iBook'dan geçtim ve bunun bir tanımını bulamadım:

Birisi yapısını açıklayabilir dispatch_aftermi?

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

1
Apple bu kitabı 2018'de yayımladı. Bulabildiğim en son arşiv Aralık 2017'den . İBook'a eski bağlantılar şimdi developer.apple.com/documentation/swift adresine yönlendirilir .
Cœur

Yanıtlar:


742

Yapı hakkında daha net bir fikir:

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

dispatch_time_tbir UInt64. Bu dispatch_queue_taslında bir türün diğer adıdır NSObject, ancak sıraları almak için yalnızca tanıdık GCD yöntemlerinizi kullanmanız gerekir. Blok bir Swift kapanışıdır. Özellikle, eşdeğer dispatch_block_tolarak tanımlanır .() -> Void() -> ()

Örnek kullanım:

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

DÜZENLE:

@ Matt'ın gerçekten güzel delayfonksiyonunu kullanmanızı tavsiye ederim .

DÜZENLEME 2:

Swift 3'te GCD için yeni ambalajlar olacak. Buraya bakın: https://github.com/apple/swift-evolution/blob/master/proposals/0088-libdispatch-for-swift3.md

Orijinal örnek Swift 3'te aşağıdaki gibi yazılacaktır:

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

Operatör aşağıdaki gibi geçersiz kılındığından (benzer şekilde ) deadlineTimebildirimi şu şekilde yazabileceğinizi DispatchTime.now() + 1.0ve aynı sonucu alabileceğinizi unutmayın :+-

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

Bu demektir ki, DispatchTimeInterval enumve sadece bir sayı yazmazsanız, saniyeler kullandığınız varsayılır.


17
İpucu: Blok işlevin son parametresi olduğundan, ekstra okunabilirlik için Swift'in "sondaki kapatma" sözdizimini kullanabilirsiniz:dispatch_after(1, dispatch_get_main_queue()) { println("test") }
Bill

8
Numaranın kullanarak düşünüyorum 1in dispatch_after(1, ...burada kargaşasına neden olabilir. İnsanlar bunun nano saniye olduğu birkaç saniye olduğunu düşünecekler . @Brindy'nin bu sayıyı doğru bir şekilde nasıl oluşturacağına dair cevabını görmenizi öneririm.
Hlung

3
Lütfen değiştirin 1için dispatch_time(DISPATCH_TIME_NOW, Int64(1 * Double(NSEC_PER_SEC)))bu karışıklığa yol açtığı için. İnsanlar Swift'te bir dispatch_time_t oluşturmanıza gerek olmadığını düşünebilirler
OemerA

4
Swift 3 sürümü çalışmıyor gibi görünüyor. On Binary operator '+' cannot be applied to operands of type DispatchTime and '_'linelet delayTime = DispatchTime.now() + .seconds(1.0)
Andy Ibanez

9
Yeniden yazmanın DispatchTime.now() + 1.0işe yaraması için tek yol olduğu anlaşılıyor (gerek yok .seconds)
Andy Ibanez

1092

Ben dispatch_aftersık sık sözdizimi kolaylaştırmak için bir üst düzey yarar işlevi yazdı kullanın:

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

Ve şimdi şöyle konuşabilirsiniz:

delay(0.4) {
    // do stuff
}

Wow, dili geliştirebileceğiniz bir dil. Daha iyi ne olabilir?


Swift 3, Xcode 8 Tohum 6 için güncelleme

Arama sözdizimini geliştirdiklerine göre, neredeyse uğraşmaya değmez gibi görünüyor:

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

2
Sadece gecikme hesaplaması için kısayola ihtiyacım vardı:func delayInSec(delay: Double) -> dispatch_time_t { return dispatch_time(DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC))) }
Aviel Gross

4
@ agf119105 Kapanışta yalnızca bir kod satırı varsa, başka bir kod satırı ekleyin (örn. return).
matt

2
@GastonM İlgisiz. Bir işlevi geçmenin kendi başına bellek yönetimi sorunu yoktur.
matt

7
Msgstr "Dili geliştirebileceğiniz bir dil". Küresel bir işlevi tanımlamanın dili nasıl geliştirdiğini veya bunun neden C'de bile yapılamayacağını anlamıyorum. Belki bir operatörü aşırı 1.0 ~~ { code...}
yüklerseniz

8
Cevabınızın doğruluğunu sorgulamamakla birlikte, "dispatch_after'ı bu kadar sık ​​kullanıyorum" değil , en iyi kolaylık işlevi sağlayarak savaşacak bir kod kokusu değil mi?
Nikolai Ruhe

128

Hızlı 3+

Bu Swift 3+'de süper kolay ve zarif:

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

Eski Cevap:

Cezary'nin 1 nanosaniyeden sonra uygulanacak cevabını genişletmek için, 4 buçuk saniye sonra idam etmek zorunda kaldım.

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)

Düzenleme: Orijinal kodumun biraz yanlış olduğunu keşfettim. Örtülü yazma, NSEC_PER_SEC öğesini bir Double'e atmazsanız derleme hatasına neden olur.

Herkes daha uygun bir çözüm önerebilirse, bunu duymak isterim.


İle kullanımdan kaldırılmış bir API için derleyici hatası alıyorum dispatch_get_current_queue(). Onun dispatch_get_main_queue()yerine kullandım .
David L

@DavidL - teşekkürler, dispatch_get_main_queue()kesinlikle kullanmanız gereken şeydir. Güncellenecek.
brindy

hızlı 3 ile bir oyun alanında bunu denedim ve işe yaramaz
μολὼν.λαβέ

@ GAlexander Benim için çalışıyor. Oyun alanının süresiz olarak çalışmasına izin veriyor musunuz?
brindy

uhm, iyi hayır, birkaç saat koşmaya bıraktım ve hala hiçbir şey basılmadı. İşte kullandığım. "import Dispatch import Darwin import CoreGraphics 'DispatchQueue.main.asyncAfter (son tarih: .now () + 4.5) {print (" burada var ")}"
μολὼν.λαβέ

83

matt'in sözdizimi çok güzel ve bloğu geçersiz kılmanız gerekiyorsa, bunu kullanmak isteyebilirsiniz:

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

Aşağıdaki gibi kullanın

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

kredi

Yukarıdaki bağlantı kesilmiş gibi görünüyor. Github'dan orijinal Objc kodu


1
PerformSelector: afterDelay özelliğine sahip bir performans özelliği onu iptal etme yeteneğidir. Sadece bu çözüm sorunu kapsar. Teşekkürler
HotJard

@HotJard performSelector:afterDelay:Swift 2'de artık mevcut olduğuna dikkat edin , böylece iptal edebilirsiniz.
matt

@matt ama sadece NSObject için mevcut, değil mi?
HotJard

@HotJard Tabii ama bu hiç sahip olmamaktan daha iyi. Orada hiçbir sorun görmüyorum. Ancak, bu cevapta olduğu gibi, zaten bir GCD tabanlı iptal edilebilir zamanlayıcı yazarak kaybını telafi etmiştim ( dispatch_source_tçünkü, bu iptal edebileceğiniz bir şeydir).
matt

2
Çok teşekkürler, bunu Swift 2.3'e kadar kullanıyorum. Swift 3.0 derleyici şimdi şikayet ediyor, cevabınızı güncellerseniz harika olurdu!
nontomatic

27

Swift 3.0 ve Swift 4.0 ve Swift 5.0'da en basit çözüm

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

kullanım

delayWithSeconds(1) {
   //Do something
}

22

Apple'ın Objective-C için bir dispatch_after snippet'i var :

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

Swift 3'e taşınan aynı snippet :

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

14

Başka bir yol, Double'u bu şekilde genişletmektir:

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

O zaman bu şekilde kullanabilirsiniz:

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

Matt'in gecikme işlevini seviyorum, ancak tercihlerin dışında geçişleri sınırlamayı tercih ederim.


8

Swift 3.0'da

Dağıtım kuyrukları

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

5 saniye sonra gönderim

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

4

Swift 3.0 sürümü

Kapatma fonksiyonunun ardından, ana iş parçacığında gecikmeden sonra bazı görevleri yürütün.

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

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

Bu işlevi şöyle adlandırın:

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

4

1) Bu yöntemi UIViewController Uzantısının bir parçası olarak ekleyin.

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

VC'de bu yöntemi çağırın:

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

// Kompakt Form

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

3

OP'nin asıl sorusu olmasa da, NSTimerilgili bazı sorular bu sorunun kopyaları olarak işaretlenmiştir, bu yüzden NSTimerburaya bir cevap eklemeye değer .

NSTimer vs dispatch_after

  • NSTimerdaha yüksek bir seviyede iken dispatch_afterdaha düşük bir seviyede.
  • NSTimeriptal etmek daha kolaydır. İptal etmek dispatch_afteriçin daha fazla kod yazmanız gerekir .

İle görevi erteleme NSTimer

Bir NSTimerörnek oluşturun .

var timer = NSTimer()

Zamanlayıcıyı ihtiyacınız olan gecikmeyle başlatın.

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

Gecikmeden sonra çağrılacak bir işlev ekleyin ( selectoryukarıdaki parametre için kullandığınız adı kullanarak ).

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

notlar

  • Eylemi gerçekleşmeden önce iptal etmeniz gerekirse, arayın timer.invalidate().
  • Tekrarlanan bir eylem için kullanın repeats: true.
  • İptal etmenize gerek olmayan tek seferlik bir etkinliğiniz varsa, timerörnek değişkeni oluşturmanıza gerek yoktur . Aşağıdakiler yeterli olacaktır:

    NSTimer.scheduledTimerWithTimeInterval(2.0, target: self, selector: #selector(delayedAction), userInfo: nil, repeats: false) 
  • Tam yanıtımı burada görebilirsiniz .


3

Birden fazla işlev için bunu kullanın. Bu, statik işlevler veya herhangi bir UI Güncellemesi için animasyonları veya Etkinlik yükleyiciyi kullanmak için çok yararlıdır.

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

Örneğin - tableView yeniden yüklenmeden önce bir animasyon kullanın. Veya animasyondan sonra başka bir kullanıcı arayüzü güncellemesi.

*// 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

Bu benim için çalıştı.

Hızlı 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)")
}

Objective-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

Hızlı 3 ve 4:

DispatchQueue üzerinde bir uzantı oluşturabilir ve DispatchQueue asyncAfter işlevini dahili olarak kullanan işlev gecikmesi ekleyebilirsiniz.

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

kullanın:

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

1

Kullanımda% 100 Swift olan ve isteğe bağlı olarak gecikmeli kodunuzu çalıştırmak için farklı bir iş parçacığı seçmenize izin veren başka bir yardımcı kod:

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

Şimdi sadece ana iş parçacığındaki kodunuzu şu şekilde geciktirirsiniz :

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

Kodunuzu farklı bir iş parçacığına ertelemek istiyorsanız :

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

Bazı daha kullanışlı özelliklere sahip bir Çerçeveyi tercih ederseniz , HandySwift'i kontrol edin . Bunu Carthage aracılığıyla projenize ekleyebilir ve yukarıdaki örneklerde olduğu gibi kullanabilirsiniz, örneğin:

import HandySwift    

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

1

Her zaman serbest işlevler yerine uzantı kullanmayı tercih ederim.

Hızlı 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()
    }
  }

}

Aşağıdaki gibi kullanın.

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

1

Hızlıca asyncAfter kullanarak GCD çağrısını erteleme

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

** mikrosaniye , milisaniye , nanosaniye olarak gecikebiliriz

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

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

1

In Swift 4

Bu snippet'i kullanın:

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

Bu zaten diğer cevaplarda (brindy, ya da Rahul's) ... aynı sözdizimi ...
Eric Aya

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

dispatch_after(_:_:_:)Fonksiyon üç parametre alır:

Bir gecikme
sıra bir sevk
bir blok ya da kapatılması

dispatch_after(_:_:_:)Fonksiyonu, belirli bir gecikme sonrasında işleve gönderme sırasına blok veya kapatma çağırır. Gecikmenin dispatch_time(_:_:)işlev kullanılarak oluşturulduğunu unutmayın . Bunu hatırlayın çünkü Swift'te de bu işlevi kullanıyoruz.

Ben Raywenderlich Dispatch öğretici geçmesi tavsiye


1

Swift 5'de aşağıdakileri kullanın:

 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

2.0 saniye sonra kullanıcı arayüzüyle ilgili bazı görevleri gerçekleştirmek için bu kodu kullanın.

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

Swift 3.0 sürümü

Kapatma fonksiyonunun ardından, ana iş parçacığında gecikmeden sonra bazı görevleri yürütün.

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

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

Bu işlevi şöyle adlandırın:

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

1
Bu önceki cevaplarla neredeyse aynı
Daniel Galasko

Bu cevap 2016 başında yapıldı ve en az diğer 6 cevap daha eski gibi görünüyor ..
eharo2

0

Şimdi Swift'teki Grand Central Dispatch (GCD) 'de eşzamansız gönderiler için sözdizimsel şekerden daha fazlası.

Podfile ekle

pod 'AsyncSwift'

Sonra, bu şekilde kullanabilirsiniz.

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, GCD'yi birkaç satırda kullanmamız için gereken her şeyi bize verdi. Neden bakla, çalışma alanı vb. @ Escaping ve Capture hakkında belgeleri okuyun. yeterli.
ingconti

0

Swift 4'ün bunu yapmanın oldukça kısa bir yolu var:

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

0

Swift'te asyncAfter'ın senkron versiyonu:

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

Eşzamansız olanla birlikte:

let deadline = DispatchTime.now() + .seconds(3)
DispatchQueue.main.asyncAfter(deadline: deadline) {
    dispatchPrecondition(condition: .onQueue(DispatchQueue.global()))
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.