0

Is there any way to use dispatch_after within a loop? I have the delay function below:

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

I want to execute it inside of a loop like this:

while true {
    self.delay(1.0) {
        // Do something repeatedly
    }
}

But I can't seem to get it working. Is it possible to do this?

Juan Boero
  • 6,281
  • 1
  • 44
  • 62
123
  • 8,733
  • 14
  • 57
  • 99

2 Answers2

1

Use a timer-type dispatch source to call the closure repeatedly. Example:

import Cocoa
import XCPlayground

func withTimerInterval(seconds: NSTimeInterval, block: dispatch_block_t) -> dispatch_source_t {
    let source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue())
    let interval = Int64(seconds * NSTimeInterval(NSEC_PER_SEC))
    dispatch_source_set_timer(source, dispatch_time(DISPATCH_TIME_NOW, interval), UInt64(interval), 20 * NSEC_PER_MSEC)
    dispatch_source_set_event_handler(source, block)
    dispatch_resume(source)
    return source
}

XCPlaygroundPage.currentPage.needsIndefiniteExecution = true

let timer = withTimerInterval(1) {
    print("hello again")
}

Cancel the timer like this:

dispatch_source_cancel(timer)
rob mayoff
  • 375,296
  • 67
  • 796
  • 848
  • Can I do this outside of a playground? – 123 Nov 26 '15 at 04:43
  • Yes, you can copy the `withTimerInterval` function into your project and use it in a real app. – rob mayoff Nov 26 '15 at 04:45
  • It says `extra argument in call` on the `withTimerInterval` line – 123 Nov 26 '15 at 04:51
  • @robmayoff Do I need to add dispatch_release(source) after canceling the dispatch source when coding in Swift? – Leo Dabus Nov 26 '15 at 05:00
  • @LeoDabus Not to speak for Rob Mayoff, but unlike a `NSTimer`, you have to keep a strong reference to the dispatch source timer in order for the repeating timer to continue to work. But when you relinquish your last strong reference to it, the source is automatically released. – Rob Nov 26 '15 at 05:04
  • @LeoDabus You cannot call `dispatch_release` at all in Swift, or [in Objective-C with ARC](http://stackoverflow.com/a/8619055/77567). – rob mayoff Nov 26 '15 at 05:27
  • @Teldridge11 Edit your question to include your new code that calls `withTimerInterval`. – rob mayoff Nov 26 '15 at 05:29
  • @robmayoff I asked because I was reading Canceling a Dispatch Source at Apple's Concurrency Programming Guide. https://developer.apple.com/library/ios/documentation/General/Conceptual/ConcurrencyProgrammingGuide/GCDWorkQueues/GCDWorkQueues.html – Leo Dabus Nov 26 '15 at 05:38
  • The programming guide is out of date with respect to reference counting of GCD objects. – rob mayoff Nov 26 '15 at 06:07
0
for (;;sleep(1)) {
    print("Hello Again");
}

*Note, this will lock your main thread every (1 second + 1 cycle) for 1 second. If this is an issue please run your whilefor loop in a separate thread, sleep that NSThread instead, and simply dispatch_get_main_queue() inside the for loop.

Albert Renshaw
  • 17,282
  • 18
  • 107
  • 195