Skip to content

Instantly share code, notes, and snippets.

@haikusw
Created June 28, 2011 03:48
Show Gist options
  • Save haikusw/1050447 to your computer and use it in GitHub Desktop.
Save haikusw/1050447 to your computer and use it in GitHub Desktop.
Objective C singleton pattern templates discussion
// Accessorizer's default generated singleton code for class Foo
static Foo *sharedInstance = nil;
+ (void) initialize
{
if (sharedInstance == nil)
sharedInstance = [[self alloc] init];
}
+ (id) sharedFoo
{
//Already set by +initialize.
return sharedInstance;
}
+ (id) allocWithZone: (NSZone *) zone
{
//Usually already set by +initialize.
@synchronized(self)
{
if (sharedInstance)
{
//The caller expects to receive a new object, so implicitly retain it
//to balance out the eventual release message.
return [sharedInstance retain];
} else
{
//When not already set, +initialize is our caller.
//It's creating the shared instance, let this go through.
return [super allocWithZone: zone];
}
}
}
- (id) init
{
//If sharedInstance is nil, +initialize is our caller, so initialize the instance.
//If it is not nil, simply return the instance without re-initializing it.
if (sharedInstance == nil)
{
self = [super init];
if (self)
{
//Initialize the instance here.
}
}
return self;
}
- (id) copyWithZone: (NSZone *) zone
{
return self;
}
- (id) retain
{
return self;
}
- (unsigned) retainCount
{
return UINT_MAX; // denotes an object that cannot be released
}
- (void) release
{
// do nothing
}
- (id) autorelease
{
return self;
}
// Apple's documented version (from Cocoa Fundamentals Guide)
// http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/CocoaFundamentals/CocoaObjects/CocoaObjects.html%23//apple_ref/doc/uid/TP40002974-CH4-SW32
static MyGizmoClass *sharedGizmoManager = nil;
+ (MyGizmoClass*)sharedManager
{
if (sharedGizmoManager == nil) {
sharedGizmoManager = [[super allocWithZone:NULL] init];
}
return sharedGizmoManager;
}
+ (id)allocWithZone:(NSZone *)zone
{
return [[self sharedManager] retain];
}
- (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;
}
@haikusw
Copy link
Author

haikusw commented Jun 28, 2011

Apple should only include best practice in their documentation. I agree that they don't always do so.
I like defensive programming.

@haikusw
Copy link
Author

haikusw commented Jun 28, 2011

thanks for chiming in schwa. That's pretty much how I wrote things by hand before I got accessorizer. I like the lazy load/creation because you can do more extensive initialization because you aren't in an +initialize context.

Seems like an easy test for sanity checking would be an NSAssert in - init that tests to see if the sharedInstance has already been created or not.

@jtbandes
Copy link

Fun addition: some Cocoa classes behave as "semi-singletons" on purpose. For example, [NSFileManager defaultManager] returns a "shared" file manager, but you can also (and apparently this is now the suggested method) use [[NSFileManager alloc] init] and get a thread-safe one. (I can't think of any other examples right now...)

@schwa
Copy link

schwa commented Jun 28, 2011

@quavera the two samples here https://gist.github.com/1050447#gistcomment-37789 and https://gist.github.com/1050447#gistcomment-37797 are really the best singleton implementations now (pre-blocks and post-blocks). I'd definitely encourage you to use them.

@quavera
Copy link

quavera commented Jun 28, 2011

BTW: I had Apple's implementation in Accessorizer for several years. Then a bunch of developers chimed in saying it was not correct. This led to Peter's version. I see now he's changed it somewhat. But again, I'm going to release Accessorizer 3.0 in Aug/Sept with ARC support among other things. I'll add a updated version of a Singleton based on your input.

@quavera
Copy link

quavera commented Jun 28, 2011

Thanks for those links!

@haikusw
Copy link
Author

haikusw commented Jun 28, 2011

editing the non blocks version to move static var out as both schwa and I generally do.

@quavera
Copy link

quavera commented Jun 28, 2011

no more overrides? I got those overrides from Apple's documentation some time ago.

@haikusw
Copy link
Author

haikusw commented Jun 28, 2011

@quavera if you read the thread the consensus here seems to be:

  1. that the Apple docs aren't great
  2. it's not necessary to enforce singleton-ness
  3. less is better.

(I'm not 100% in agreement with #2, and will probably put an NSAssert into my - init that tests to see if the sharedInstance is nil and assert if not. Something like NSAssert( sharedInstance == nil, @"initializing a second copy of your singleton class, probably not what you want"); either as an assert or a debug NSLog message.)

@schwa
Copy link

schwa commented Jun 28, 2011

no more overrides.

they're dangerous and scramble the normal ref counting mechanism (which isn't even available to you in ARC anyway).

If the user wants to retain/release the singleton - let him. It won't cause any harm at all.

If the user decides to release it - well that's a stupid programmer who isn't following the Obj-C reference counting rules. Not much you can or should do to prevent that.

ARC makes all this noise go away.

@quavera
Copy link

quavera commented Jun 28, 2011

thanks to all for this discussion - I have a few switches (options) in the Accessorizer Singleton code gen interface. I can re-implement/add some custom options there. People just need to email me what they want and I'll do my best to accommodate. Lastly, if there's ever a question with the code generated by Accessorizer, do NOT hesitate to contact me via email so we can discuss, modify, improve etc.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment