Re: Is Apple's singleton sample code correct?
Re: Is Apple's singleton sample code correct?
- Subject: Re: Is Apple's singleton sample code correct?
- From: Dietmar Planitzer <email@hidden>
- Date: Sat, 3 Dec 2005 00:24:24 +0100
On Dec 2, 2005, at 7:48 PM, Shaun Wexler wrote:
Yes, there are three obvious bugs, but they aren't in the ref-
counting methods. Ref-counting has been REMOVED from the singleton
class, so "standard Cocoa" rules don't apply [internally] because
the object has become immortal. The sample code is correct.
Shaun, I fear that you have really missed my point. Please go back
and read especially the last part of my mail. Then it should be clear
to you why I'm against overriding the reference counting methods in
the way that the Apple sample code is doing it.
Let's make it short. I have a very simple question for you:
What is the sound and important technical reason for overriding the
reference counting methods in the case of a singleton ?
What is the big technical advantage of doing that, especially when
compared to the code that I presented in my original mail ? The code
in my original mail is able to implement a singleton with a lifetime
equal to the lifetime of the application by just overriding 3
methods. That's less than half the number of methods that the Apple
code overrides. Considering the famous KISS engineering principle,
why should it be better to override more than 6 methods when the same
end result can be achieved by overriding just 3 methods ?
I'm sorry but I don't see the point. I've been programming with ObjC
for more than 10 years now (seems like that mentioning such private
information is somehow considered important..) and I've never had any
reason to override -retain, -release or -autorelease in all that time
so that they do nothing. Neither in the case of a regular object nor
in the case of a singleton.
The first bug is that if -dealloc is sent to it directly, it will
be freed, so -dealloc must be overridden to become a NOP, ala -
release. That method should be added to the sample code.
Could you please give me a sound technical reason why overriding -
dealloc in the case of a singleton so that it does nothing is
supposed to be a good idea ?
I personally think that it is not only unnecessary and that it
needlessly complicates the code base, but I also think that it makes
it unnecessarily harder to find a memory management bug in the code
that is using the singleton.
The reason why I think that doing this is not a good idea is that
code which calls a -dealloc method directly, no matter on what
object, severely violates the CMM rules. Thus, I should be able to
find this problem efficiently and in short order. But overriding -
dealloc so that it does nothing makes it unnecessarily hard for me to
find this problem as it gets swept under the rug. At least, when you
really want to write your code that way, make sure that dealloc
raises an appropriate exception so that we get a chance at fixing
code that is using the singleton in an inappropriate way.
The second bug is that the recursive mutex @synchronized(self)
needs to be @synchronized([MyGizmoClass class]) in both class
methods, because the static variable sharedGizmoManager is what the
mutex has to protect, because two subclasses created simultaneously
on two threads permits a race situation. The sample code should be
updated and documented to reflect this.
Well, I think that the sample code should concentrate on the core
issue of writing a singleton. I don't think that it's such a good
idea that it drags side issues like multi threading in which has
nothing to do with the general and basic question of how a singleton
is typically implemented in the case of Cocoa. It needlessly
complicates the sample code and distracts from the actual problem.
This is in fact the reason why I have dropped the thread-safety stuff
altogether in my code. It just distracts.
The third bug is that the design is messy, and doesn't allow
subclassing. Both +sharedManager and +allocWithZone: allow
assignment of sharedGizmoManager, and it reassigns it (and hits the
lock twice) when instantiated properly. According to the last
comment, this is intended to permit optional usage. The sample
code should be augmented with 2 or 3 other singleton designs, with
examples. Also, the +sharedManager methods should be more
efficient because they are messaged often, and entering the mutex
should be avoided if the static var is valid, plus an early exit
minimizes the cache hits to load the unnecessary code each use.
+ (MyGizmoClass*)sharedManager
{
if (sharedGizmoManager) {
return sharedGizmoManager;
}
@synchronized(self) {
if (sharedGizmoManager == nil) {
sharedGizmoManager = [[self alloc] init];
}
}
return sharedGizmoManager;
}
Hmm, please give me a sound technical explanation why you think that
the double-check locking idiom works reliably in ObjC. I'm asking you
because I know that it does not work reliably in Java, C and C++ and
I can't remember that Apple has refined the definition of the ObjC
language so that it gives any guarantee with regards to the order in
which the compiler generates instructions for a nested message send
expression. Nor do I remember that the language gives any guarantee
about the exact time when a value is written from a register to a
global variable.
Therefor, I'm interested in whether Apple has indeed changed the
language in such a way that the idiom above is guaranteed to work.
Now, I do know that there are quite a bunch of changes to the runtime
waiting in the wings. If you want to know more about this, then
simply head over to the GCC SVN repository and check out the ObjC
related parts of the GCC compiler in the Apple branch. There's a lot
of very interesting stuff to be found there, if you dig deep enough.
Readers who don't yet know what the double-check locking idiom is may
simply enter that phrase in Google. You'll get tons of documents that
describe what it is and why it does not work in the "standard"
programming languages.
The short explanation is that:
1) Thread #1 calls +sharedManager, sees that the singleton doesn't
exist yet, enters the synchronized block, calls +alloc and writes the
result back to the variable sharedGizmoManager.
2) Now a context switch happens from Thread #1 to Thread #2.
3) Thread #2 calls +sharedManager and by executing the first if
statement sees that the singleton does already exists.
4) Thread #2 now happily goes on to use the singleton, which however
has not yet been initialized by Thread #1 because the OS scheduler
decided to demote it from running state to ready state...
Really, this thread is entirely too silly, but with all of the C++/
WIN switchers to come, it's better to explain it correctly in the
archives, or we'll have to go thru it again and again.
Honestly I don't know why you call this thread entirely too silly.
The only problem that I see with this thread is that at the beginning
there was a very simple question. However, although a lot has been
said in this thread, and a lot of topics have been brought up that
have absolutely nothing to do with the original question, so far
nobody has given a simple, clear and sound technical explanation why
it is necessary to override the reference counting methods. Note that
I said, necessary. Since quite a number of people have sided with the
Apple sample code, I have to assume that there must be a really
important reason that it overrides -retain, -release and -autorelease
and that by doing so it achieves something really important that
would not be achievable by implementing the singleton using the
standard CMM rules like I did in my code.
So please, could someone answer this simple question : What is the
seemingly so important advantage of overriding the reference counting
methods ?
Or formulated in a different way: why should the implementation of a
singleton not follow the standard CMM rules ?
And just to make it clear: I'm not going to accept answers like, it
makes ref counting faster or it makes it possible to violate the CMM
rules in the implementation of the singleton's +alloc and -copy
methods, or that is how NeXT did it. I'm sorry, but those are not
sound technical reasons for me.
Regards,
Dietmar Planitzer
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Cocoa-dev mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden