Skip to content

Instantly share code, notes, and snippets.

@jstn
Created June 10, 2014 16:55
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jstn/0dedc76b287af09d1ddb to your computer and use it in GitHub Desktop.
Save jstn/0dedc76b287af09d1ddb to your computer and use it in GitHub Desktop.
// JSTN / 10 June 2014
// I really like Swift so far, but I can't help be a little sad that I still find
// myself doing the weakSelf/strongSelf dance to avoid retain cycles.
// For a contrived example, suppose we had a class like this with a queue:
import Foundation
class ParallelMessenger {
let queue = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT)
func message(text: String) {
dispatch_async(queue) {
NSLog(text, nil)
}
}
// Now suppose we wanted to count every message in a thread-safe way:
var messageCount = 0
func messageAndCount(text: String) {
// We'll need to refer to self a couple times here, but we don't want
// any messages to get logged if self has been deallocated by the
// time the block executes. Swift has a nice syntax that allows you
// to make any references to self within a closure automatically weak:
dispatch_async(queue) { [weak self] in
// So self is now a weak reference, meaning it becomes nil if self
// gets deallocated. Since we're on a private queue, it's possible
// for self to disappear at any moment, so to keep working with it
// we need to a create a strong reference:
if let strongSelf = self {
NSLog(text, nil)
// We can't increment strongSelf.messageCount because we're on a
// concurrent queue and can't risk memory contention. Instead we
// can dispatch a barrier block, which will execute serially as
// soon as any concurrently running blocks finish.
// Here again we need the same weak reference behavior, only now
// the syntax makes less sense because we'd wind up with a
// weak reference to something called strongSelf. So let's
// do it manually, not that's it much prettier. Ugh.
weak var weakSelf = strongSelf
dispatch_barrier_async(strongSelf.queue) {
if let strongSelf = weakSelf {
strongSelf.messageCount++ // Finally!
}
}
}
}
}
}
// In some class designs this actually isn't the behavior you want, like if you
// needed to perform some task regardless of whether self still exists (such as
// closing a file descriptor). Again, this is a contrived example.
// Still, I find myself doing this enough that I wish there was a more concise
// way to express it, and without variables called "weakSelf" and "strongSelf",
// which seem at odds with the safely typed nature of Swift.
// Maybe something like this:
// dispatch_async(queue) { [autoweak self] in ... }
// Meaning, "make a weak reference to self that is safe to use (i.e. strong)
// within the current scope."
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment