Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is this the definitive ref counted Objective C singleton implementation?

Here is what I have concocted, after poring over the singleton literature. Have I forgotten anything?

@implementation MySingleton

static MySingleton *mySharedInstance = nil;

//called by atexit on exit, to ensure all resources are freed properly (not just memory)  
static void singleton_remover()
{
    //free resources here
}

+ (MySingleton*) sharedInstance{
    return mySharedInstance;
}

+ (void)initialize {
    if (self == [MySingleton class]) {
        mySharedInstance = [[super allocWithZone:NULL] init];
    atexit( singleton_remover );    
    }
}

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

- (id)copyWithZone:(NSZone *)zone
{
    return self;    
}

- (id)retain
{
    return self;    
}

- (NSUInteger)retainCount
{
    return NSUIntegerMax;  //denotes an object that cannot be released  
}

- (void)release
{
    //do nothing    
}

- (id)autorelease
{
    return self;    
}
like image 287
Jacko Avatar asked Feb 19 '10 01:02

Jacko


2 Answers

which avoids the synchronize lock most of the time

If you want your software to be reliable, avoid constructs that work "most of the time"

http://developer.apple.com/mac/library/DOCUMENTATION/Cocoa/Conceptual/Multithreading/ThreadSafety/ThreadSafety.html

Table 4.1. Double-checked lock

A double-checked lock is an attempt to reduce the overhead of taking a lock by testing the locking criteria prior to taking the lock. Because double-checked locks are potentially unsafe, the system does not provide explicit support for them and their use is discouraged.

like image 68
glebm Avatar answered Oct 07 '22 17:10

glebm


A few suggestions (more for Mac Cocoa than iPhone, but it may be useful to other people searching the objective-c tag):

  • Don't bother with -allocWithZone:NULL, just plain -alloc will do fine.
  • Consider using dispatch_once() or pthread_once() where available
  • The atexit usage is clever but may not be compatible with fast app termination (not sure if this applies on the iPhone), since that effectively kill -9s the app

One additional fun pattern:

+ (Foo *)sharedFoo {
    static Foo *sharedInstance = NULL;
    if (!sharedInstance) {
        Foo *temp = [[Foo alloc] init]; //NOTE: This MUST NOT have side effects for it to be threadsafe
        if (!OSAtomicCompareAndSwapPtrBarrier(NULL, temp, &sharedInstance)) {
            [temp release];
        }
    }
    return sharedInstance;
}
like image 23
Catfish_Man Avatar answered Oct 07 '22 17:10

Catfish_Man