Singletons in Swift
I recently learned that Swift makes it really simple to make a singleton.
In Objective-C, we’re all used to the dispatch_once()
pattern for singletons,
and things like this.
@implementation C
+ (instancetype)sharedInstance
{
static dispatch_once_t onceToken;
static id sSharedInstance = nil;
dispatch_once(&onceToken, ^{
sSharedInstance = [[self alloc] init];
});
return sSharedInstance;
}
// Rest of the class implementation
@end
But, in Swift, it’s even easier. Basically, if you do a static let
property
in your class (Swift’s documentation calls that a “type property”), then you
can treat that like a singleton.
The Swift equivalent of the above Objective-C code is:
class C {
static let sharedInstance = C()
// Rest of the class implementation
}
// later, call it with
let c = C.sharedInstance
Much simpler!
Some Details
Swift automatically builds these type properties lazily (without requiring the
lazy
keyword. From the docs:Global constants and variables are always computed lazily, in a similar manner to Lazy Stored Properties. Unlike lazy stored properties, global constants and variables do not need to be marked with the lazy modifier.
I think it effectively does the
dispatch_once()
for you (or, the equivalent thereof), although I can’t find that in the docs.You can use
class
instead ofstatic
, to let subclasses override them. That’s a subtle distinction, but could be useful. Again, from the docs:For computed type properties for class types, you can use the class keyword instead to allow subclasses to override the superclass’s implementation.
Another Example
Here’s a more realistic example, using a NSDateFormatter
, from a Playground:
import Cocoa
class C {
// You can even use a closure to build your singleton (and remember, it's done lazily)
static let formatter: NSDateFormatter = {
print("building date formatter")
usleep(100)
let f = NSDateFormatter()
f.dateFormat = "MMMM YYYY"
return f
}()
func formatDate(date: NSDate) -> String {
return C.formatter.stringFromDate(date)
}
func doSomethingWithoutFormatter() {
print("doSomethingWithoutFormatter()")
}
}
let c = C()
// use the instance to see that the formatter hasn't been built yet
c.doSomethingWithoutFormatter()
// do a bunch of concurrent blocks that will all try to access the formatter
for _ in 0..<100 {
dispatch_async(dispatch_get_global_queue(0, 0)) {
print(c.formatDate(NSDate()))
}
}
// Make the playground environment run GCD queues.
dispatch_main()
The output is:
doSomethingWithoutFormatter()
building date formatter
June 2015
June 2015
June 2015
...
You can see a few interesting things:
It doesn’t run the block to build the formatter when the class is first
init
ed. Instead, it waits until it is accessed for the first time.Even with the
sleep()
when building the formatter, and thedispatch_async()
s running all of the blocks concurrently, it only builds the formatter once. If you run it in a Playground, you can tell that all of the other dispatched blocks are waiting on the first one to finish (because they’re all blocked behind the implicitdispatch_once()
).
Other Tricks
You can make the type property use var
, and then modify it later.
import Cocoa
class C {
// You can even use a closure to build your singleton (and remember, it's done lazily!)
static var formatter: NSDateFormatter = {
let f = NSDateFormatter()
f.dateFormat = "MMMM YYYY"
return f
}()
func formatDate(date: NSDate) -> String {
return C.formatter.stringFromDate(date)
}
}
let c = C()
for _ in 0..<100 {
dispatch_async(dispatch_get_global_queue(0, 0)) {
print(c.formatDate(NSDate()))
}
}
let newFormatter = NSDateFormatter()
newFormatter.dateFormat = "YYYY MMMM"
C.formatter = newFormatter
// Make the playground environment run GCD queues.
dispatch_main()
The output is:
2015 June
June 2015
June 2015
June 2015
June 2015
June 2015
June 2015
2015 June
2015 June
2015 June
2015 June
2015 June
2015 June
June 2015
2015 June
2015 June
2015 June
June 2015
2015 June
2015 June
June 2015
June 2015
2015 June
2015 June
2015 June
2015 June
June 2015
2015 June
June 2015
2015 June
June 2015
2015 June
June 2015
2015 June
2015 June
... (the rest are all the same "2015 June")
You can see the first few block executions raced to get the formatter before or after the change.
I don’t have a great example of when this would be useful, but I imagine it could be.
Also, because I haven’t been able to find any details of how this works
internally, I’m not sure how thread-safe it is to actually set
and get
this
value from different threads concurrently (as I do in the example above).
Presumably it has some sort of atomic
in the accessors, but I wouldn’t suggest
using this in production code, without further experimentation or research.
Thanks
Thanks again to @jtbandes for help with this.
Update: 2015-07-28
I just saw this
post (linked from
This Week in
Swift), where Hector
Matos comes to the same one-line conclusion, and uses some breakpoints to prove
that the dispatch_once()
is actually being used. Nice.
Also, he reminds us to use a private init()
, to prevent callers from creating
instances without going through the singleton.
class C {
static let sharedInstance = C()
private init() {} // <-- Force callers to use the singleton.
// Rest of the class implementation
}