Re: Proper way to create a singleton without @synchronized ?
Re: Proper way to create a singleton without @synchronized ?
- Subject: Re: Proper way to create a singleton without @synchronized ?
- From: WT <email@hidden>
- Date: Sun, 17 Apr 2011 02:20:45 -0300
Hi Kyle, thanks for replying.
On Apr 17, 2011, at 12:31 AM, Kyle Sluder wrote:
> Do you really need a singleton, or just a default instance?
A singleton.
>> static MySingleton* stSharedInstance = nil;
>>
>> static dispatch_once_t stSharedInstanceInvoked;
>> static dispatch_once_t stAllocWithZoneInvoked;
>> static dispatch_once_t stInitInvoked;
>
> All of these could be made static variables within the method body,
> increasing readability and reducing pollution of the global scope
> without affecting storage longevity.
It's the coding style I'm used to, listing the statically allocated variables at the top. I don't see how declaring them inside the methods that use them helps to reduce pollution of the global scope, since they have to be visible to different methods in the same compilation unit anyway.
>> + (MySingleton*) sharedInstance;
>> {
>> dispatch_once(&stSharedInstanceInvoked,
>> ^{
>> NSLog(@"+sharedInstance block entered");
>>
>> // Assignment done in +allocWithZone:.
>> // The autorelease message is sent to prevent XCode's
>> // static analyzer from issuing a warning about a possible
>> // memory leak. There is no leak, since the singleton is
>> // not meant to be deallocated, and the autorelease message
>> // does nothing, as per its overridden implementation below.
>
> Rather than relying on -allocWithZone: to return the shared instance,
> why not assign to stSharedInstance here?
No reason other than that this implementation is based on the singleton implementation I saw in some Apple documentation.
>> [[[[self class] alloc] init] autorelease];
>
> You are in a class method. self == [self class], so no need to call +class here.
What if I'm trying to subclass this singleton?
>> + (id) allocWithZone: (NSZone*) zone;
>> {
>> dispatch_once(&stAllocWithZoneInvoked,
>> ^{
>> NSLog(@"+allocWithZone: block entered");
>>
>> stSharedInstance = [super allocWithZone: zone];
>>
>> NSLog(@"+allocWithZone: block exited");
>> });
>>
>> return stSharedInstance;
>
> Of course this means you can't subclass this class.
Why not? As long as the subclass doesn't override +allocWithZone:, this shouldn't be a problem, no?
>> - (id) init;
>> {
>> __block id tempSelf = self;
>
> Because the block is not going to be copied, you do not need to worry
> about creating a __block version of self here. And since this is a
> singleton, even if the block *were* copied (and thus self retained),
> you still wouldn't care.
>
>>
>> dispatch_once(&stInitInvoked,
>> ^{
>> NSLog(@"-init block entered");
>>
>> tempSelf = [super init];
>>
>> if (tempSelf)
>> {
>> someInteger_ = random() % 1000;
>> NSLog(@"Instance initialized");
>> }
>>
>> NSLog(@"-init block exited");
>> });
>>
>> self = tempSelf;
>> return self;
>> }
My first attempt was simply
>> - (id) init;
>> {
>> dispatch_once(&stInitInvoked,
>> ^{
>> NSLog(@"-init block entered");
>>
>> self = [super init];
>>
>> if (self)
>> {
>> someInteger_ = random() % 1000;
>> NSLog(@"Instance initialized");
>> }
>>
>> NSLog(@"-init block exited");
>> });
>>
>> return self;
>> }
but the compiler complained that self is read-only within the scope of the block. I needed a way to write to self and I couldn't redeclare self, hence a writable tempSelf.
>> - (id) copyWithZone: (NSZone*) zone;
>> {
>> return self;
>
> By virtue of this implementation, you can't implement NSCopying in a
> subclass. But you can't subclass this class anyway because of
> +allocWithZone:.
>
>> }
I can see why that might cause some problems but, then, semantically, a singleton shouldn't be something that you can duplicate anyway.
>> - (unsigned) retainCount;
>> {
>> return UINT_MAX; // Denotes an object that cannot be released.
>
> Why bother? All this will do is mask any bugs where your singleton disappears.
>
>> }
>>
>>
>> - (void) release;
>> {
>> /* do nothing */
>
> Don't do this. As above, you're just masking cases where you
> accidentally over-release your singleton.
>
>> }
About these two, once again it all came from Apple's implementation in one of the docs. I read it, it made sense to me at the time, so I used it often. Now, I wanted to replace the use of @synchronized but didn't think of changing anything I didn't absolutely have to change.
>> - (id) autorelease;
>> {
>> return self;
>> }
>
> Look, you're going to great lengths to ensure you never create more
> than one instance of this class. Is any of it really necessary?
Well, it is if I need a true singleton and sometimes I do. In fact, I kinda often do. I tend to break down my code into classes with very well defined, and typically few, responsibilities. Often times, making them singletons simplifies my code.
For instance, I never put all the core data stack methods in the application delegate. I have a singleton that takes care of all things core data (well, all things that can be done generically). Likewise, I have a singleton for locale "utilities". All my number and date formatters are there, in one place, created once on demand and accessible everywhere else through the singleton. That's the kind of usage I have for singletons.
Granted, sometimes they don't *have* to be singletons (they don't represent expensive resources) and I could just as well have a default instance. The locale utilities is such an example. Then, again, why allow for that possibility when it's logically sound that I should only ever need one instance?
Rather than go into a philosophical discussion of the kind "when/why singletons?", let me rephrase and focus my original plea for help: how would you implement a *true* singleton class that supports subclassing?
Thanks again for your input.
WT_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden