Weak Self -- Closure Rules of Thumb

In Swift, you can weak-ify references to self in escaping closures, and then you need to deal with the case that the reference is gone when the block is called. Last month, Benoit Pasquier and Chris Downie presented different takes on the problem. That discussion was excellent. It prompted me to take some more time to revisit this problem systematically, and I took away a couple of notes for future-me.

Continue reading …

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 …

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 …