Thursday, April 07, 2011

Singletons: You're doing them wrong

This post feels a bit odd, as generally I fully agree with Dave Dribin on how wrong singletons are and that you should avoid them if possible. However on Stack Overflow i've seen far too many people write bad code that is horrible for several reasons.

Basic Example

Singletons are fairly easy to create. Here is a basic example...

+(MyClass *)singleton {
 static MyClass *shared = nil;
 
 if(shared == nil) {
  shared = [[MyClass alloc] init];
 }
 return shared;
}

However this is wrong on several levels. Firstly, this isn't thread safe, so what happens if multiple threads all try to access this at the same time? There is no reason 1 thread couldn't be in the middle of allocating the object while the other one is trying to access the object. This is actually what Apple shows in its documentation.

If you must use singletons, use dispatch_once()

dispatch_once() solves the problem of safely being able to create a singleton in that (1) it guarantees that the code in the block will only be called once for the lifetime of the application (2) its thread safe as I noted in a previous article and (3) its faster than other methods like using @synchronize(),etc...

"If called simultaneously from multiple threads, this function waits synchronously until the block has completed."

So you should be writing it like this...

+(MyClass *)singleton {
 static dispatch_once_t pred;
 static MyClass *shared = nil;
 
 dispatch_once(&pred, ^{
  shared = [[MyClass alloc] init];
 });
 return shared;
}

This is a safe way to create a singleton. There is no way to accidentally create it twice, its fast and it is thread safe.

Conclusion

Avoid singletons if at all possible because it is easy to abuse them. Unlike Dave Dribin I don't think they are evil, but I really try and avoid them when possible. If you must create them, do it right and don't create them like the first example, use dispatch_once(). It is less code and the correct way to do it.

 
...