-
-
Save haikusw/1050447 to your computer and use it in GitHub Desktop.
// 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; | |
} |
Interesting.
- How is it an enforced singleton if we allow other copies to be made?
- I guess if we aren't overriding alloc to return the shared already allocated object then the retain/release part is as you say. But see 1, :)
Yeah, I guess it is more of an ad-hoc singleton, than an enforced one. But I submit the following: if you are using an enforced singleton, there isn't a whole lot of difference between that and just sending messages to a class, instead of a single instance of that class.
agreed re "it's just a class object at that point"...
Can't say as I've seen anyone making class objects with class data and a bunch of class methods for something like that though... and something to block creation of actual instances.
interesting thoughts.
So, it seems like the non-blocks version of what you wrote would be:
static GizmoManager *manager = nil;
+ (GizmoManager *)sharedGizmoManager {
@synchronized(self) {
if ( manager == nil )
manager = [[self alloc] init];
return manager;
}
}
or you could break it into two and do the allocate in +initialize
I suppose.
if one wanted the non-forced-singleton that allowed other instances (which I'm still feeling conflicted about, though I see your point).
Yeah... (One definite drawback of the class-level singleton is that subtype polymorphism won't work like you expect it to, since all your “members” are really static variables.)
Though subclassing a singleton class seems like shifting sands definitionally...
now I find myself wanting to write @synchronized( [GizmoManager class] )
in - init
since we've synchronized on the class object itself... and that just seems odd, though probably legal...
@Accessorizer wrote: Accessorizer has 2 singleton gens: 1 based on PeterHosy: http://t.co/Ltpw66R and a GCD version on Mike Ash: http://t.co/w2W2KGt
-init should be used for initialization - NOT for singleton management.
Also, accessorizer's default singleton is just utterly horrible. It's completely trying to bypass Obj-C's memory management. What if something in init fails? The correct behavior is for init to release itself and return nil - nope sorry - this bastardized singleton can't do that. release is a NOP and retainCount is effectively infinite. Not cool.
Best to do the LEAST work you can - and that's where Jon's dispatch_once version or your @synchronized verison fomes in. It's just ONE new class method you can add to any class.
Accessorizer's is pretty close to what Apple's docs have, fwiw. see the second one above, that's straight out of Apple's documentation and it does the same thing.
He got it from: http://boredzo.org/blog/archives/2009-06-17/doing-it-wrong which is an interesting discussion that has some holes in it on my reading. But still makes some interesting arguments.
so schwa - without blocking retain/release and infecting - init you have something that isn't necessarily a singleton. It's cool that there can be more than one? I suppose one could assert in - init ...
fascinating that this isn't clear cut and documented.
Apple's docs are wrong. As simple as that.
As I mentioned above init can fail.
I don't think Apple example code should always be considered best practice. Esp. when it is pretty easy to pick holes in. Quite frankly I wish Apple would remove this code form their docs - it really is bad code. And with ARC out now - it isn't even relevant.
I've never seen a bug in 12+ years of Cocoa programming that would have been avoided by overriding the reference counting methods.
I suspect it will be documented soon, given the remarks made in the Writing Easy to Change Code session, but we'll see...
Hi folks - yes, I got that Singleton code from Peter Hosey and it seemed in 2009, he had a compelling case against what Apple was suggesting. I since added GCD a la Mike Ash. I can improve the Singleton (long version) for Accessorizer 3.0 once we can all agree is THE best way. I'm all ears. Send me what you think is the correct and best/safest way to write a non-GCD based singleton. Apple's documentation on Singleton code has changed from time to time. Is their current version THE correct way? -Thanks, Kevin
I use the + (Foo *)sharedInstance; method and normal memory management rules.
99% of the time i never retain or release the singleton explicitly - it gets lazy init-ed in the sharedInstance method and then effectively leaked. If dealloc has to do some clean up work then I'll hook it up to the applicationWillTerminate: notification and clean it up there (note - for this reason I keep the static variable OUT of the sharedinstance block so it can be released and nil-ed elsewhere).
The 1% of the time the singleton is retained/released the normal memory management rules work just fine. If you retain it, you ought to release it.
I avoid copying the singleton by simply just not adopting the NSCopying protocol.
Really - less is more here. Just use the simple sharedInstance approach and move on with your life. If you use common sense and follow the normal Cocoa patterns you won't see any singleton related bugs.
Apple should only include best practice in their documentation. I agree that they don't always do so.
I like defensive programming.
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.
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...)
@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.
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.
Thanks for those links!
editing the non blocks version to move static var out as both schwa and I generally do.
no more overrides? I got those overrides from Apple's documentation some time ago.
@quavera if you read the thread the consensus here seems to be:
- that the Apple docs aren't great
- it's not necessary to enforce singleton-ness
- 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.)
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.
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.
In answer to your other question, it doesn't prevent other copies from being created. Because we really shouldn't be doing that.