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: David Gimeno Gost <email@hidden>
- Date: Sat, 26 Nov 2005 16:21:44 +0100
mmalcolm crawford <email@hidden> wrote:
Can you point me to any document describing the singleton design
pattern saying that a singleton should never be deallocated or
destroyed?
<http://developer.apple.com/cgi-bin/search.pl?
q=singleton&site=default_collection>
I already did that search before starting this discussion and I haven't
found anything there stating that a singleton should never be
deallocated. Actually, I would consider it a bug if I found it because
there is nothing in the singleton design pattern requiring it to never
be destroyed and there are indeed reasons why you may want to actually
be allowed to destroy it when appropriate.
<http://developer.apple.com/documentation/Cocoa/Conceptual/
CocoaDesignPatterns/Articles/HowCocoaAdaptsDP.html>
What this article says is: "Using the shared instance returned by a
singleton class is no different from using an instance of a
non-singleton class, except that you are prevented from copying,
retaining, or releasing it". This is under the "Framework Classes"
heading, not in the general description of the pattern.
I take it as saying that the singletons provided by Cocoa are designed
to never be deallocated. That's fine, I have no problems with that. But
if you are saying that I must take it as meaning that preventing the
singleton to be deallocated is part of the design pattern then I would
consider it a bug in the documentation because there is nothing in the
singleton pattern, as it is widely known, that requires it to never be
deallocated. Moreover, as a general rule, I would even say that it
_should_ be deallocated when the program ends if for no other reason
because the resources it manages may need to be properly released.
That the particular adaptation of the singleton pattern made in the
Cocoa frameworks does not allow for their singletons to be deallocated
does not change what I'm saying in any way. You may have all the
variations of the pattern that you see fit your particular needs. But
they are variations, they are not part of the pattern as it is widely
known.
But you don't need to override the methods to accomplish that. What
I'm saying is that the default (inherited) methods already do the
right job if users of the class follow the Cocoa's memory
management rules.
This is not the case. If you didn't override release, then
(following Cocoa's memory management rules):
MyGizmoClass *gizmo = [[MyGizmoClass alloc] init];
// do some stuff
[gizmo release];
// do some more stuff
MyGizmoClass *gizmo2 = [[MyGizmoClass alloc] init];
[gizmo2 doSomething]; // crash
At this point, sharedGizmoManager is a pointer to a freed object.
Not if +allocWithZone: is written as I later suggested in the same
e-mail you are responding to here. Note the added -retain message sent
to the shared instance if it already exists:
+ (id) allocWithZone: (NSZone*) zone
{
@synchronized( self ) {
if ( sharedGizmoManager == nil ) {
sharedGizmoManager = [super allocWithZone: zone];
} else {
[sharedGizmoManager retain];
}
}
return sharedGizmoManager;
}
2. The -retain and -release methods shouldn't be overridden. There
is no point in doing so and the way it is done in the current
sample code guarantees that code breaking Cocoa's memory management
rules will go by unnoticed.
The whole point of a singleton object is that, if it is created, it
remains valid for the remainder of the lifetime of the application.
The whole point of the singleton is that there is at most one instance
of it. The lifetime of the object is not part of the pattern. You may
choose to create it at program startup or only when needed. You may
choose to destroy it when the resources it manages are no longer
needed, or when the program exits, or never destroy it at all. These
are all design issues and implementation details that must be
considered. They are not requirements. I've yet to come across a
reference document that says preventing the singleton from being
destroyed is part of the pattern.
It just happens that it is appropriate for most singletons to remain
valid for the lifetime of the application, but even that doesn't mean
the singleton shouldn't be allowed to be deallocated, because, as a
general rule, it should at least be possible to deallocate it when the
application quits. And you don't need to do anything special to ensure
that. The only requirement is that both the singleton implementation
and the code that uses it must adhere to Cocoa's memory management
rules. Letting the default -retain and -release methods do their job
allows for all lifetime variations of the pattern. There is no need to
allow only a single variation that happens to be wrong for singletons
that manage resources that should be properly released and that has the
side effect of hiding buggy client code that doesn't follow the memory
management rules.
Overriding the retain and (in particular) release methods means that
(as Shawn suggested) a developer can continue to adhere to the normal
rules of memory management without needing to "special case" any
objects. This typically simplifies things.
You don't need to consider them a "special case" if you don't want to.
If you want client code to be able to invoke [[alloc] init], and
retain/release them as any other object just make sure that your
implementation of +allocWithZone: remains consistent with those
requirements (by retaining an already existing instance the caller is
expected to release later).
This is what really simplifies things because (1) it puts the solution
where the problem is located (in the +allocWithZone: method), rather
than spreading it over several methods, (2) it will work correctly
regardless of whether you choose to override those additional methods
or not, i.e. you don't have to override them if you don't have a
(another) good reason to do so, (3) it doesn't arbitrarily impose a
lifetime requirement on the singleton that may not be appropriate in
some cases, and (4) it lets bugs in the memory management of client
code be detected during development and testing.
Sample code should strive to be applicable to as many variations as
possible while still remaining as clear and simple as possible. The
implementation I'm proposing achieves both objectives, the current
sample code provided by Apple achieves neither.
Regards.
_______________________________________________
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