PDA

View Full Version : keeping a singleton




Sydde
May 24, 2011, 05:01 PM
I have a singleton object that is approximately equivalent to something like @"" which I want to keep around. So I am looking at this, to save me having to check the object in other code:
- (void)dealloc {
if ( self != theSingleltonObject ) {
// release my ivars
[super dealloc];
}
}
I have observed that you do not seem to need -retain to prevent an object from being deallocated, just bypass the [super dealloc]. Is this behavior consistent? In my experiment, when I included -retain in the -dealloc method, it seemed to increment the retain count (the object required 2 releases to go away). Not that this would matter in this case, I was just curious if anyone knew whether I can count on not needing the -retain?



gnasher729
May 24, 2011, 07:00 PM
I have a singleton object that is approximately equivalent to something like @"" which I want to keep around. So I am looking at this, to save me having to check the object in other code:
- (void)dealloc {
if ( self != theSingleltonObject ) {
// release my ivars
[super dealloc];
}
}
I have observed that you do not seem to need -retain to prevent an object from being deallocated, just bypass the [super dealloc]. Is this behavior consistent? In my experiment, when I included -retain in the -dealloc method, it seemed to increment the retain count (the object required 2 releases to go away). Not that this would matter in this case, I was just curious if anyone knew whether I can count on not needing the -retain?

Don't mess around with dealloc.

You create a singleton in the +initialize method (don't forget to check [self class] so you don't create another one when a subclass is initialized); that makes it threadsafe at no cost as well. Without compiling, so fix all errors first:

static MyClass* gMyClassSingleton;
+ (void)initialize
{
if ([self class] == [MyClass class]) {
gMySingleton = [[MyClass alloc] init];
}
}

+ (MyClass*)sharedMyClass {
return gMySingleton;
}


Then you rely on standard memory management. Anyone can call [myClass sharedMyClass], use the result, and retain and release it. If it is released too often, it goes bang! like any other object would. I don't bother doing anything special in init, but if you like you can force [[MyClass alloc] init] to return the singleton object if you do

- (id)init
{
if (gMyClassSingleton != nil && self != nil && [self class] == [MyClass class]) {
[self release];
return [gMyClassSingleton retain];
}
// The usual code goes here
}

Sydde
May 24, 2011, 09:21 PM
Don't mess around with dealloc.

You create a singleton in the +initialize method
That may be what you do. I find it much simpler to use the lazy method:

static id singletonObject = nil;

+ (id)blankThing {
if ( singletonObject == nil ) singletonObject =[[myClass alloc] initWithValue:@""];
return singletonObject;
}


This way, I do not have to check whether this is the right class or not. The singleton is only visible to that one method (and other methods in that .m file).
Then you rely on standard memory management. Anyone can call [myClass sharedMyClass], use the result, and retain and release it. If it is released too often, it goes bang! like any other object would.
That is the problem. I do not want it to go "bang!". Besides conditionally bypassing [super dealloc]; is there another way to protect a singleton from going away?

lee1210
May 24, 2011, 09:59 PM
http://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CocoaFundamentals/CocoaObjects/CocoaObjects.html#//apple_ref/doc/uid/TP40002974-CH4-SW32

Apple suggests overriding retain, release, autorelease, et al for your singleton class.

-Lee

jiminaus
May 25, 2011, 02:02 AM
static id singletonObject = nil;

+ (id)blankThing {
if ( singletonObject == nil ) singletonObject =[[myClass alloc] initWithValue:@""];
return singletonObject;
}



If you do it this way, I don't see why you need to muck around with dealloc. You should always have an excess retain count because the alloc here is never matched with a dealloc. So dealloc should never be called.

If you're coding a library that's going to be used by other unknown code, then overriding retain, release, autorelease as @lee1210 suggest is the better way. Personally, I use gnasher729's approach.

Who knows what other housekeeping as already been cleaned-up by the runtime by the time it calls your dealloc, or after. And what are the consequences of creating a zombie object; particularly for future versions of the runtime.

gnasher729
May 25, 2011, 02:13 AM
That may be what you do. I find it much simpler to use the lazy method:

Now make your code thread safe.

+initialize is _exactly_ there for that kind of thing. +initialize is called lazily just before the first message is dispatched to your class, and in a thread-safe way. And it is a well-known (maybe not well-known enough) design pattern.

Sydde
May 25, 2011, 07:56 AM
And what are the consequences of creating a zombie object; particularly for future versions of the runtime.

I am not sure that zombie objects exist except in a GC environment, and then only if you use -finalize improperly. I mean, an object is nothing more than a context frame. If isa is intact (the only ivar belonging to NSObject, other than perhaps retainCount), the runtime will look at that and figure out how to dispatch methods. If you bypass your regular -dealloc that releases ivars and calls [super dealloc], the context frame has not been modified or freed, so it should still be a working object. I just cannot imagine what happens between -release and -dealloc, other than the change in retain count, that would affect the validity of an object. After all, the reference is just a pointer to a frame.

Nonetheless, I will use overrides of -release and -autorelease, just to be safe.

robbieduncan
May 25, 2011, 08:07 AM
Now make your code thread safe.

+initialize is _exactly_ there for that kind of thing. +initialize is called lazily just before the first message is dispatched to your class, and in a thread-safe way. And it is a well-known (maybe not well-known enough) design pattern.

The other option, if you are in an environment that support Grand Central Dispatch, is to use dispatch_once (http://developer.apple.com/library/mac/documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html#//apple_ref/c/func/dispatch_once).