I need this explanation to educate our programmers on the reason some of our
code works with StepStone's compiler on the VAX but breaks under NEXTSTEP on
NeXTs. Please forward any replies to my email address below and I will
summarize for the net and repost.
Thanks!
--
Bob Lunney
Encompass
+1 919 460 3274
b...@bobsled.cen.encompass.com NeXTmail preferred!
>At one time there was an elegant, succinct posting describing the difference
>between using +new and +alloc -init to allocate and initialize object under
>NEXTSTEP. I know you should use +new when you want one and only one instance
>of an object ever in you program, as with Application, say. However, I cannot
>find the detailed reason why anyway on the 'net.
If you simply use alloc then init, you can't check for the existence of
the object before it gets alloc'ed. If you use a single +new method (or
something similar), then you can check for the existence of the object before
it gets allocated in memory. Here is some code from my nuclear power plant
simulation written for my masters thesis. It checks for the existence of
the NuclearPowerSystem, and if it doesn't exist, it will create itself,
and attach a reactor core to itself, then it returns it's instance. If it
exists already, it just returns it's instance:
@implementation NuclearPowerSystem
+ create
{
static NuclearPowerSystem *instance = nil;
/* Assure only one instance of the nuclear power system */
if (instance == nil) {
instance = [[self alloc] init];
instance->reactorCore =
[[NuclearReactorCore alloc] initWithPowerSystem:instance];
}
return instance;
}
.....
Eric
>Here is some code from my nuclear power plant
>simulation written for my masters thesis. It checks for the existence of
>the NuclearPowerSystem, and if it doesn't exist, it will create itself,
>and attach a reactor core to itself, then it returns it's instance. If it
>exists already, it just returns it's instance:
>@implementation NuclearPowerSystem
>+ create
>{
> static NuclearPowerSystem *instance = nil;
>
> /* Assure only one instance of the nuclear power system */
>
> if (instance == nil) {
> instance = [[self alloc] init];
> instance->reactorCore =
> [[NuclearReactorCore alloc] initWithPowerSystem:instance];
> }
>
> return instance;
>}
I forgot to say that you must keep in mind that you are inside of a *class*
method when programming things in this manner. The class object does not
know about instance variables directly, like instance methods do. Therefore,
I had to access the instance variable 'reactorCore' by using the ->
operator like this: instance->reactorCore. If this were an instance
method (like -init), then I could have access reactorCore directly.
Eric
Why not adopt the guideline that ivars are accessed only via
mutator and accessor method "pairs?"
Then, you don't need to be able to access them directly.
--
personal opinions
Question: What happens with this code:
-----------------------------------------
#import "NuclearPowerSystem.h"
@interface MyNuclearPowerSystem : NuclearPowerSystem
@end
@implementation MyNuclearPowerSystem
@end
void main()
{
id np = [NuclearPowerSystem new];
id mnp = [MyNuclearPowerSystem new];
}
------------------------------------------
How do you "fix" it?
Ralph
--
Ralph Zazula
Developer Trainer
NeXT, Inc.
Ralph_...@next.com
(415) 780-2893
Ooops, I meant to use the +create method in my code snippet, not +new.
Here is my question re-posed with the proper code.
Ralph
Eric M Hermanson writes
>
> @implementation NuclearPowerSystem
>
> + create
> {
> static NuclearPowerSystem *instance = nil;
>
> /* Assure only one instance of the nuclear power system */
>
> if (instance == nil) {
> instance = [[self alloc] init];
> instance->reactorCore =
> [[NuclearReactorCore alloc] initWithPowerSystem:instance];
> }
>
> return instance;
> }
>
> .....
>
>
> Eric
>
Question: What happens with this code:
-----------------------------------------
#import "NuclearPowerSystem.h"
@interface MyNuclearPowerSystem : NuclearPowerSystem
@end
@implementation MyNuclearPowerSystem
@end
void main()
{
id np = [NuclearPowerSystem create];
id mnp = [MyNuclearPowerSystem create];
You get only one nuclear power system. (And it's of whatever class you
called +new on first.)
> How do you "fix" it?
Sometimes this is what you want, but to get the other behaviour (ie
subclasses get their own instance) you need to do something like:
+ new
{
static HashTable *instances;
id instance;
if (instance = [instances valueForKey:self])
return instance;
instance = [[self alloc] init];
if (!instances)
instances = [[HashTable alloc] init];
[instances insertKey:self value:instance];
return instance;
}
Keep a hash table to map class -> instance and do essentially the same
thing as in a "normal" +new method.
--Greg
----------
Greg Titus
Omni Development Inc.
to...@omnigroup.com
Right! You need to have a way for each subclass to get the proper
instance. In the example I gave, the line of code:
id mnp = [MyNuclearPowerSystem new];
would have returned an instance of the NuclearPowerSystem class, *not* a
MyNuclearPowerSystem. This is a side-effect of using static data in your
classes (static really means static - it stays the same...).
Another interesting twist on this is implementing methods like:
+ powerSystemWithName:(const char *)name;
in a "subclass safe" way. The idea is to create a new instance if 'name'
is new and reuse an old one if 'name' has already been created...
Anyway, I thought this topic might be interesting to the net.collective as
it comes up repeatedly in our courses...
Also, it was pointed out to me in an e-mail that the MiscKit has a class
that does class-variable "emulation" using HashTable's as described above.
See you at EXPO...
You get only one nuclear power system. (And it's of whatever class you
called +new on first.)
> How do you "fix" it?
Sometimes this is what you want, but to get the other behaviour (ie
subclasses get their own instance) you need to do something like:
+ new
{
static HashTable *instances;
id instance;
if (instance = [instances valueForKey:self])
return instance;
instance = [[self alloc] init];
if (!instances)
instances = [[HashTable alloc] init];
[instances insertKey:self value:instance];
return instance;
}
Keep a hash table to map class -> instance and do essentially the same
thing as in a "normal" +new method.
--Greg
----------
Greg Titus
Omni Development Inc.
to...@omnigroup.com
>
Generally, when you have a class method that returns the same instance (i.e.
+new or +create), you should override that in a subclass if you also want to
use that funcationality. Unless you implement the method in such a way as to
allocate an instance of the class that was used as the target of the method and
you are prepared to live with the fact that the class used for the first
invocation will be the class of which an instance is returned. A third
solution depending on your needs is to use -poseAs: with your subclass.
You definitely have to be careful in these situations.
--
_______________________________________________________________
/ Eric Brown | The opinions expressed here \
| NEXTSTEP Consultant | are mine and do not necessarily |
| CG Computer Services | represent those of my employer |
| er...@il.us.swissbank.com | or SBC. |
\___________________________|___________________________________/
This can be done by having each entry in the class-keyed static-based
hashtable refer not to the real PowerStation object but to a secondary
name-keyed hashtable of named PowerStation objects that are local to each
subclass. The following example uses MiscKit's MiscClassVariable as the
static class-keyed hashtable - the nice thing about MiscKit's object is
that it neatly encapsulates the hashtable into a pair of methods called
setObject:forClass and getObjectForClass:. My example expands it to
include the second layer of hashtable. Note however, that this is still
merely a hack (albeit a neat hack) of the real problem of not having class
variables.
static MiscClassVariable powerPlants;
+ initialize
{
if ([SuperPowerPlants class] == self) {
// Creates the pseudo-class variable powerPlants.
// It is only done for the Superclass.
powerPlants = [[MiscClassVariable init] alloc];
}
// The following is done for ALL subclasses of SuperPowerPlants
// assuming each appropriately calls [self initialize].
// It creates a HashTable as a pseudo-class variable for each
// subclass which maps char* keys (names) to objects.
HashTable *subclassHashTable = [[HashTable alloc] initKeyDesc:"*"];
[powerPlants setObject:subclassHashTable forClass:self];
return self;
}
+ setPowerPlant:plant forName:(const char*)name
{
HashTable *subclassHashTable = [powerPlants getObjectForClass:self];
[subclassHashTable insertKey:name value:plant];
return self;
}
+ powerPlantForName:(const char*)name
{
HashTable *subclassHashTable = [powerPlants getObjectForClass:self];
return [subclassHashTable valueForKey:name];
}
> Anyway, I thought this topic might be interesting to the net.collective
> as it comes up repeatedly in our courses...
>
> Also, it was pointed out to me in an e-mail that the MiscKit has a class
> that does class-variable "emulation" using HashTable's as described
> above.
It was and it is.
Jon Rosen