views:

142

answers:

3

I have looked on the web but can't find anything that might help. My question is can I write the tires[x] array as an NSArray, if so what would be the syntax to both declare and allocate the tire Class instance objects?

@interface CarClass : NSObject {
    EngineClass *engine;
    TireClass *tires[4];  
}

.

@implementation CarClass
- (id) init {
    [super init];
    NSLog(@"_init: %@", NSStringFromClass([self class]));
    engine= [[EngineClass alloc] init];
    tires[0]= [[TireClass alloc] init];
    tires[1]= [[TireClass alloc] init];
    tires[2]= [[TireClass alloc] init];
    tires[3]= [[TireClass alloc] init];
    return self;
}

EDIT:

this is my dealloc method for the CarClass

- (void) dealloc {
    NSLog(@"_deal: %@", NSStringFromClass([self class]));
    [engine release];
    [tires release];
    [super dealloc];
}

Still a bit confused about the retain in the NSArray below, if I add an extra [tires retain] to the CarClass:init then the tires do not get released. However as the code is now the tires release with or without the end retain (i.e.)

tires = [[NSArray arrayWithObjects:
        [[[TireClass alloc] init] autorelease],
        [[[TireClass alloc] init] autorelease],
        [[[TireClass alloc] init] autorelease],
        [[[TireClass alloc] init] autorelease],
        nil] retain];

tires = [NSArray arrayWithObjects:
        [[[TireClass alloc] init] autorelease],
        [[[TireClass alloc] init] autorelease],
        [[[TireClass alloc] init] autorelease],
        [[[TireClass alloc] init] autorelease],
        nil];

FINAL EDIT: I also thought that without the autorelease on the individual tires that finally releasing the NSArray in the dealloc would release both the array and the objects it points to, this does not seem to be the case.

cheers -gary-

+4  A: 
@interface CarClass : NSObject {
    EngineClass *engine;
    NSArray *tires;
}

.

@implementation CarClass
- (id) init {
    self = [super init];
    if (self == nil)
        return nil;
    NSLog(@"_init: %@", NSStringFromClass([self class]));
    engine= [[EngineClass alloc] init];
    tires = [[NSArray arrayWithObjects:
        [[[TireClass alloc] init] autorelease],
        [[[TireClass alloc] init] autorelease],
        [[[TireClass alloc] init] autorelease],
        [[[TireClass alloc] init] autorelease],
        nil] retain];
    return self;
}
Nikolai Ruhe
Have I got this right, you don't alloc the array, but rather the objects it contains? Sorry just trying to get my head around it.
fuzzygoat
The code is calling the `+[NSArray arrayWithObjects:]` method, which is a convenience method that automatically allocates, then autoreleases, the array object (hence the attached call to `retain`).
mipadi
@mipadi can you explain the retain bit, am I right in thinking the code is retaining the NSArray, the objects are later autoreleased. Just not sure what happens to the NSArray and why its retained?
fuzzygoat
@fuzzygoat: A class needs to own its instance variables. Without a retain there, it doesn't own the array. If you want to do any programming in Cocoa, you need to read the memory management rules. They're quite simple and you're never going to get it 100% right if you don't. http://developer.apple.com/mac/library/DOCUMENTATION/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html
Chuck
fuzzygoat
just looked at the tires NSArray again and it is created with a retain count of 1, does that mean it is owned by the class within which it was created? the final retain bumps that to 2, which to my mind would them require 2 releases to dispose of it? OR maybe I don't worry about that and simply matching the retain (in my init) to the release (in my dealloc) is sufficient.
fuzzygoat
After a little more reading, I am now thinking what happens is that when the NSArray is created it is not owned by me, the retain (in my init) then says I want to take ownership, matched later by the release (in my dealloc) that says I want to give up the object .... sound plausible to anyone?
fuzzygoat
The memory management in the code it right. What happens on the array is: `arrayWithObjects` gives you an autoreleased object (actual retain count is one, but will be released to zero after next pass through the event loop). Then `retain` increments the retain count to two, granting ownership to the class. The [[[TireClass alloc] init] autorelease] creates autoreleased tires, since the tire owner is the array.
Nikolai Ruhe
Again: read the "Memory Management Rules", you're lost without! You especially have to understand AutoreleasePools, which do not exist in all refcounting environments.
Nikolai Ruhe
A: 

You don't always need to use an NSArray for collections. How about this way which has a bit more information about each tyre:

@interface CarClass : NSObject {
    EngineClass *engine;
    NSDictionary *tiresDictionary;
}

@implementation CarClass
- (id) init {
    self = [super init];
    if (!self) {
        return nil;
    }
    NSLog(@"_init: %@", NSStringFromClass([self class]));
    engine= [[EngineClass alloc] init];
    tiresDictionary = [[NSDictionary dictionaryWithObjectsAndKeys:
    [[[TireClass alloc] init] autorelease], @"LeftFrontTyre",
    [[[TireClass alloc] init] autorelease], @"RightFrontTyre",
    [[[TireClass alloc] init] autorelease], @"LeftRearTyre",
    [[[TireClass alloc] init] autorelease], @"RightRearTyre",
    nil] retain];
    return self;
}

This way you still have a collection class, but rather than try and remember which index of an array refers to which tyre, you have a dictionary with descriptive keys so you can refer to each tyre by a name.

Abizern
should "tires = [[NSDictionary" be "tiresDictionary = [[NSDictionary", not an expert by far but looks like it should be.
fuzzygoat
Yep. Fixed. Shows why copy and paste editing is error prone!
Abizern
If you're putting it all in a dictionary...why not just have separate instance variables like `TireClass *leftFrontTire`, `TireClass *rightFrontTire`, etc.?
mipadi
The original question was how to store them in an NSArray; I just wanted to point out that there are other collections that the tyres could be put in. The implementation details will cause on or the other to be chosen.
Abizern
I am just learning right now, so all ideas are welcome, many thanks.
fuzzygoat
@mipadi: iteration over tires.
Nikolai Ruhe
A: 

I would change Nikolai's example a bit. Since I do not want to add autorelease and retain where none is needed, less code is always less bug-prone code.

@interface CarClass : NSObject {
  EngineClass *engine;
  NSArray *tires;
}

@implementation CarClass
- (id) init {
  self = [super init];
  if (self) {
    NSLog(@"_init: %@", NSStringFromClass([self class]));
    engine= [[EngineClass alloc] init];
    tires = [[NSArray alloc] initWithObjects:
        [TireClass new], [TireClass new], [TireClass new], [TireClass new], nil];
    [tires makeObjectsPerormSelector:@selector(release)];
  }
  return self;
}

The [tires makeObjectsPerormSelector:@selector(release)]; can be scary, but is useful and also very performant. A slightly less performant way, but maybe more clear would be to let the TireClass implement a factory method, and create autoreleased objects from that one. Like so:

+(TireClass)tire;
{
  return [[self new] autorelease];
}

Also please stop naming your classes "Something"Class, a kitten dies if you do :)

PeyloW
Thank you, I will drop the class name, its more to help learning than a conscious style choice at this point. Many thanks for your thoughts.
fuzzygoat