— boreal-kiss.net

Subclassing a singleton in a thread-safe manner

Why subclassing? Because I’m bored by preparing a bare-bone singleton from scratch.

+[NSObject initialize] is invoked only once in a thread-safe manner before a class object receives any message (for more detail see NSObject Class Reference). Therefore this method could be a good place for a singleton instance to be created.

Here is my singleton class. I won’t show the complete implementation here. To be robust you should control methods concerning allocation such as alloc, init, copy, etc. However these are not of importance here. To check these visit the following site:

//BKSingleton.h
 
#import <Foundation/Foundation.h>
 
@interface BKSingleton : NSObject{
 
}
 
/**
 * Returns a singleton instance.
 */
+(id)sharedInstance;
 
@end
//BKSingleton.m
 
#import "BKSingleton.h"
 
static NSMutableDictionary *_dictionary = nil;
 
@implementation BKSingleton
 
+(id)sharedInstance{
	NSString *className = NSStringFromClass(self);
	return [_dictionary objectForKey:className];
}
 
+(void)initialize{
	if (!_dictionary){
		_dictionary = [[NSMutableDictionary dictionary] retain];
	}
 
	NSString *className = NSStringFromClass(self);
 
	if (![_dictionary objectForKey:className]){
		[_dictionary setObject:[[[self alloc] init] autorelease] forKey:className];
	}
}
 
@end

When a subclass of this class is first received a message, the initialize method of this class is invoked (i.e., +[BKSingleton initialize]) unless this is overridden in the subclass.

The first trick here is that there is no hard-coded class names inside the initialize method (e.g., something like [MyClass alloc]). Thanks to this even the subclass is received its superclass’s initialize method, it is always instantiated by the correct class.

The second trick is that the instance of a subclass is stored in a static class dictionary associated with its class name if there is not such an object with the same key found in the dictionary. Doing this ensures that the dictionary holds a unique instance for each subclass.

Let’s see an example. There are two subclasses inherited from BKSingleton. They have nothing newly implemented.

//Singleton1
 
#import "BKSingleton.h"
 
@interface Singleton1 : BKSingleton{
 
}
 
@end
 
@implementation Singleton1
 
@end
//Singleton2
 
#import "BKSingleton.h"
 
@interface Singleton2 : BKSingleton{
 
}
 
@end
 
@implementation Singleton2
 
@end

Then run the following code.

//main.m
 
#import <Foundation/Foundation.h>
#import "BKSingleton.h"
#import "Singleton1.h"
#import "Singleton2.h"
 
int main(int argc, char *argv[]) {
	NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
 
	NSLog(@"BKSingleton: %@", [BKSingleton sharedInstance]);
	NSLog(@"Singleton1: %@", [Singleton1 sharedInstance]);
	NSLog(@"Singleton2: %@", [Singleton2 sharedInstance]);
 
	//Once again
	NSLog(@"BKSingleton: %@", [BKSingleton sharedInstance]);
	NSLog(@"Singleton1: %@", [Singleton1 sharedInstance]);
	NSLog(@"Singleton2: %@", [Singleton2 sharedInstance]);
 
	[pool release];
	return 0;
}

The output is the following, ensuring they are al unique, i.e., singletons.

Test[1153:207] BKSingleton: <BKSingleton: 0xe0a880>
Test[1153:207] Singleton1: <Singleton1: 0xe08d20>
Test[1153:207] Singleton2: <Singleton2: 0xe0b620>
Test[1153:207] BKSingleton: <BKSingleton: 0xe0a880>
Test[1153:207] Singleton1: <Singleton1: 0xe08d20>
Test[1153:207] Singleton2: <Singleton2: 0xe0b620>

Related links

0 comments
Submit comment

Get Adobe Flash player