FTSWAbstractSingleton

last edited February 20, 2008 13:15:28 (91.135.10.146)
CocoaDev is sponsored by: Panic: Shockingly good Mac software!

SingletonDesignPattern:Implementation

Hope folks find this code useful. Please point out any defects or improvements. -ShawnErickson

The following code is an implementation of a base class that allows sub-classes to inherit singleton behavior including preventing deallocation of the singleton instance and related enforcement of the contract. This implementation is also thread safe.

(Are you sure? See: http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf) - Yes, this implementation uses synchronize (grab a lock) and not any broken double check, etc. scheme as outline in the PDF you referenced.

FTSWAbstractSingleton.h

#import <Foundation/Foundation.h>

@interface FTSWAbstractSingleton : NSObject {
}
+ (id)singleton;
+ (id)singletonWithZone:(NSZone*)zone;

//designated initializer, subclasses must implement and call supers implementation
- (id)initSingleton; 
@end

FTSWAbstractSingleton.m

#import "FTSWAbstractSingleton.h"

@implementation FTSWAbstractSingleton

static NSMutableDictionary  *s_FTSWAbstractSingleton_singletons = nil;

+ (void)initialize
{
    @synchronized([FTSWAbstractSingleton class]) {
        if (s_FTSWAbstractSingleton_singletons == nil) {
            s_FTSWAbstractSingleton_singletons = [[NSMutableDictionary alloc] init];
        }
    }
}

// Should be considered private to the abstract singleton class, 
// wrap with a "sharedXxx" class method
+ (id)singleton
{
    return [self singletonWithZone:[self zone]];
}

// Should be considered private to the abstract singleton class
+ (id)singletonWithZone:(NSZone*)zone
{
    id singleton = nil;
    Class class = [self class];
    
    if (class == [FTSWAbstractSingleton class]) {
        [NSException raise:NSInternalInconsistencyException
                    format:@"Not valid to request the abstract singleton."];
    }
    
    @synchronized([FTSWAbstractSingleton class]) {
        singleton = [s_FTSWAbstractSingleton_singletons objectForKey:class];
        if (singleton == nil) {
            singleton = NSAllocateObject(class, 0U, zone);
            if ((singleton = [singleton initSingleton]) != nil) {
                [s_FTSWAbstractSingleton_singletons setObject:singleton forKey:class];
            }
        }
    }
    
    return singleton;
}

// Designated initializer for instances. If subclasses override they
// must call this implementation.
- (id)initSingleton
{
    return [super init];
}

// Disallow the normal default initializer for instances.
- (id)init
{
    [self doesNotRecognizeSelector:_cmd];
}

// ------------------------------------------------------------------------------
// The following overrides attempt to enforce singleton behavior.

+ (id)new
{
    return [self singleton];
}

+ (id)allocWithZone:(NSZone *)zone
{
    return [self singletonWithZone:zone];
}

+ (id)alloc
{
    return [self singleton];
}

- (id)copy
{
    //[self doesNotRecognizeSelector:_cmd]; //optional, do if you want to force certain usage pattern
    return self;
}

- (id)copyWithZone:(NSZone *)zone
{
    //[self doesNotRecognizeSelector:_cmd]; //optional, do if you want to force certain usage pattern
    return self;
}

- (id)mutableCopy
{
    //[self doesNotRecognizeSelector:_cmd]; //optional, do if you want to force certain usage pattern
    return self;
}

- (id)mutableCopyWithZone:(NSZone *)zone
{
    //[self doesNotRecognizeSelector:_cmd]; //optional, do if you want to force certain usage pattern
    return self;
}

- (unsigned)retainCount
{
    return UINT_MAX;
}

- (oneway void)release
{
}

- (id)retain
{
    return self;
}

- (id)autorelease
{
    return self;
}

- (void)dealloc
{
    [self doesNotRecognizeSelector:_cmd];  //optional, do if you want to force certain usage pattern
}
// ------------------------------------------------------------------------------

@end


The following is an example sub-class...

#import "FTSWAbstractSingleton.h"

@interface MySingleton : FTSWAbstractSingleton {
    ...blah...
}
+ (MySingleton*)sharedMySingleton;
...blah...
@end

@implementation MySingleton

+ (MySingleton*) sharedMySingleton
{
    static MySingleton *s_MySingleton = nil;
    
    @synchronized([MySingleton class]) {
        if (s_MySingleton == nil) {
            s_MySingleton = [self singleton];
        }
    }
    
    return s_MySingleton;
    
    // or you could not use the static var above and just do "return [self singleton];"
}

- (id)initSingleton
{
    if (self = [super initSingleton]) {
        ...blah...
    }
    
    return self;
}

...blah...
@end