TIL You Can Cancel Enqueued GCD Blocks

Today I learned that you can cancel a delayed dispatch_block_t with the new dispatch_block_cancel (available since OS X 10.10/iOS 8.0). Thanks Matt for the post – here’s a Swift example:

let work = dispatch_block_create(0) { print("Hello!") }

# Execute after 10s
let delayTime = dispatch_time(DISPATCH_TIME_NOW, Int64(10 * Double(NSEC_PER_SEC)))
dispatch_after(delayTime, dispatch_get_main_queue(), work)

dispatch_block_cancel(work)
# Will never print "Hello!"

Note: canceling doesn’t work if the block is being executed.

If I knew that this API existed, I might not have used the very cumbersome approach from below in Move!.

Super-Weird Legacy Version of a Cancelable Delayed Block

For historic purposes, here’s an adaptation of the cancelable dispatch block you may find on the internet that I once have adapted for Swift:

typealias CancelableDispatchBlock = (cancel: Bool) -> Void

func dispatch(cancelableBlock block: dispatch_block_t, atDate date: NSDate) -> CancelableDispatchBlock? {

    // Use two pointers for the same block handle to make
    // the block reference itself.
    var cancelableBlock: CancelableDispatchBlock? = nil

    let delayBlock: CancelableDispatchBlock = { cancel in
    
        if !cancel {
            dispatch_async(dispatch_get_main_queue(), block)
        }
    
        cancelableBlock = nil
    }

    cancelableBlock = delayBlock

    let interval = Int64(date.timeIntervalSinceNow)
    let delay = interval * Int64(NSEC_PER_SEC)

    dispatch_after(dispatch_walltime(nil, delay), dispatch_get_main_queue()) {
    
        guard let cancelableBlock = cancelableBlock else { return }

        cancelableBlock(cancel: false)
    }

    return cancelableBlock
}

func cancelBlock(block: CancelableDispatchBlock?) {

    guard let block = block else { return }
    
    block(cancel: true)
}

The trick is this: the delayed block delayBlock: CancelableDispatchBlock captures its context where a reference to cancelableBlock is included – but not set yet. Then you make the reference point to the delayBlock itself.

The actual canceling is a fake, though. The block is still called. It aborts early if the cancel parameter is true, though.

Blocks: Useful to Perform Actions In Context

Something short and sweet I want to share with you because I love how it reads: restoringSelection is a function that takes a block and performs whatever the block does while restoring the current selection in a table view (here: NSTableView). Here’s the implementation. The resulting code from above reads so much nicer than querying selection() first and restoring it afterwards. It communicates very clearly that some state from before will be preserved (or restored).

Continue reading …

Block-Based API vs. Delegates – a Comparison

As I’m exploring the use of block based API, which means to assign closures or functions handles to properties or pass them around as parameters to other functions, I found a few benefits and drawbacks in comparison to protocol-based object interactions. Here’s a breakdown of criteria for blocks and delegates.

Continue reading …

Separating State from State Changes

Reflecting on a recent change of the Word Counter’s file monitoring module, I think I re-discovered a commonly advised pattern in my code: to separate state from state changes. There’s an object that knows how to handle files based on extension: plain text files’s words are counted differently than Word or Scrivener files. Call it Registry. Previously, this was set up once and didn’t change. Now I wanted to make this configurable so users can add custom plain text extensions. This means changing that object’s state.

Continue reading …

How Closures are a Better Event Handler Protocol Alternative

I don’t like the way I tend to create view controller event handlers. They are almost always just a sink for methods which have nothing in common conceptually but are tied together because of the view’s capabilities. So I began experimenting. Closures can encapsulate changes. This works well with callbacks for Repositories which fetch entities from your data store. Instead of returning them, you can pass them forward:

Continue reading …