//
you're reading...

Programming

Two Macros to Speed Your Singletons

SingletonSingletons are great – when used properly. They simplifies things when you need is only one instance of a class without resorting to make all methods static. For desktop/mobile apps (that is single-user-per-running-instance types), I find singletons great for service or manager type classes:

  • Coordinating refresh of thumbnail images and make sure not to ask for the same thing twice.
  • Handling the menu extra item.
  • Handling services menu requests.
  • Responding to GPS location changes.
  • Connecting to the back-end service (when you only have one endpoint).

Even Apple seemingly agrees with this. We see singleton classes in Cocoa all the time – NSNotificationCenterNSDocumentController, even the application delegate is a singleton (in a sense). Singletons may be not so great in web applications since it means sharing state between users and limit scalability. But for software where the running instance only serves one user, it really simplifies a lot of stuff.

Some people say that singletons is a bad thing since it’s a “global” state and global states are a bad thing. True that you shouldn’t make singletons out of everything – but there are many cases in which singletons simplifies your code. Particularly since you don’t need to worry to assign ownership to that instance or do the “pass the baton” approach between view/window controllers – as long as there is no need for more than one instance of the class.

A few purists may even go further as overriding retain/release to disallow more than one instance of the class to be created. But really I think this is a bit too far. In Objective-C, all code that runs within a single process is trusted (or at least assumed to be trusted), and you don’t really need to enforce access control up to this level. If you’re going to run untrusted code, it’s better to do it in a separate process – and there are mechanisms in Mac OS X to facilitate you to do this.

Hence I propose a simple singleton instead of a pure singleton and code appropriately:

  • There is a “default” instance of the class.
  • The class doesn’t assume that there is only one instance.
  • You can create additional instances of the class if needed.

One well-known example of a simple singleton is NSFileManager. There is one “default” instance of it, accessed via the defaultManager class method. You can also create additional instances of it in case you need them.

Creating Singletons

Now, since simple singletons are pretty much standard, why not automate coding these gems instead of writing them out each time? I have two macros that you can use to make just any class a “simple” singleton:

  • DECLARE_SIMPLE_SINGLETON_FOR_CLASS(className) – place this in a header file to declare the singleton.
  • SYNTHESIZE_SIMPLE_SINGLETON_FOR_CLASS(className) – place this in the corresponding implementation file to create the actual method.

Adding this pair of macros creates a category called “SimpleSingleton” for that class and adds one class method defaultInstance that you use to access the singleton instance. Note that this is a simple singleton, not a pure or “proper” singleton and thus nothing prevents you from creating additional instances of the class.

You can do this to just about any class as long as it can be instantiated via init or new. This also enforces a good habit to code your classes so that they can be instantiated without any fuss – just use new.

You can even use this to make singletons out of system classes. In fact one of my major use of these macros is to create a simple singleton out of NSOperationQueue. Often I need to do something in a background thread but not really keen to do thread management or even create a dispatch queue (and have something that owns that queue). A “simple” singleton fits this task nicely since there is one “default” operation queue that you can use for most purposes.

Usually I follow the following pattern of passing work to background threads:

-(IBAction) commandHandler:(id) sender
{
    // … we're in the main thread … 
    [[NSOperationQueue defaultInstance] addOperationWithBlock:^{
    	// … do stuff the background … 
	    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
		    // … then pass the result to the main queue to display in the UI … 
    	}];
    }];
}

Usually everything starts from the main thread. Then whenever there’s something isolated that can take a long time to run (e.g. more than half a second), it’s a good idea to do it in a background thread so that it doesn’t freeze the user interface. So we call upon our default operation queue to run that code block in the background. When it’s done, we’ll run another block that runs in the main thread and updates some state in the user interface.

So here are the magical macros to speed up your singleton creation.

#define DECLARE_SIMPLE_SINGLETON_FOR_CLASS(classname) \
\
@interface classname (SimpleSingleton)
\
+ (instancetype) defaultInstance;
\
@end

#define SYNTHESIZE_SIMPLE_SINGLETON_FOR_CLASS(classname) \
\
@implementation classname (SimpleSingleton) \
\
+ (instancetype)defaultInstance { \
static classname *classInstance = nil; \
static dispatch_once_t classInstanceDispatch = 0; \
dispatch_once(&classInstanceDispatch, ^{    \
classInstance = [self new]; \
}); \
\
return classInstance; \
} \
@end

Notice that the synthesize part uses dispatch_once to create the singleton. This is the preferred way to serialize singleton creation and make sure that there aren’t any race conditions in case defaultInstance is first called in a background thread.

Another benefit of using macros to create singletons is to enforce convention. You don’t really need to think whether to call [XXFooManager defaultFooManager]  or [XXFooManager defaultManager] – just call defaultInstance (wonder why Apple didn’t do it this way? Perhaps it’s just one of those carry-overs from the NeXT days).

Until next time. Take care.



Do you enjoy this post? Enter your e-mail address below to receive articles like this one in your mailbox.
* indicates required

Discussion

No comments yet.

Leave a Reply

Free Updates!

Learn how to grow your indie business while keeping your day job.

Categories

Archives

Keep updated!

Don't miss out on new articles!