Objective-c – Array Via Setter

objective c

This is just a test to help me learn Objective-C, it uses NSMutableArray to add tire objects to an instance variable in a car object:

// INTERFACE
@interface CarBody : NSObject {
 NSMutableArray *tires;
}
// Should this be (id *) it works but I was convinced it would be pointer?
- (void) addTire:(id)newTire;
@end

@interface TireSnow : NSObject {
}
@end

// IMPLEMENTATION
@implementation CarBody
- (void) addTire:(id)newTire {
 [tires addObject:newTire];
    // ** Release here or in main()?

}
- (id) init {
 [super init];
 tires = [[NSMutableArray alloc] init];
 NSLog(@"_init: %@", NSStringFromClass([self class]));
 return self;
}
- (void) dealloc {
 NSLog(@"_deal: %@", NSStringFromClass([self class]));
 [tires release];
 [super dealloc];
}
@end

I do have a few questions …

  1. In the addTire method, is the (id) right, I thought it was going to be (id *)
  2. Releasing the item I am adding to the array, should I do it inside the setter or in main() after I call it?
  3. Am I allocating / releasing the NSMutableArray (tires) in the right place, it feels right?
  4. Is there a way to do this with NSArray (as I only want 4 tires), I did try this but got mixed up trying to alloc the array and define its size.

thanks in advance for any help …

gary

EDIT:

I am reading the memory management rules, but they do take some time to absorb and do require a certain level of understanding that is difficult to gain when starting out. What I am wondering about in this situation is where would I release the newSnowTire that I alloc in main. When I add it to the array in the setter does that create a new object in the array (thats my understanding) so my thinking was that I would need to release the instance I got from alloc?

// MAIN
#import <Foundation/Foundation.h>

int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    CarBody *newCarBody_001;
    TireSnow *newSnowTire_001;

    newCarBody_001 = [[CarBody alloc] init];
    newSnowTire_001 = [[TireSnow alloc] init];

    [newCarBody_001 addTire:newSnowTire_001];

    // Clean up
    [newCarBody_001 release];
    [newSnowTire_001 release];
    [pool drain];
    return 0;
}

EDIT_002:

Just added the code to generate all 4 tires with the tire release moved into the loop after the setter is called.

// CREATE TIRES
for(int loopCounter=0; loopCounter<4; loopCounter++) {
    newSnowTire_001 = [[TireSnow alloc] init];
    [newCarBody_001 addTire:newSnowTire_001];
    [newSnowTire_001 release];
}

I just checked this and it is correct …

  1. NewSnowTire_001 (alloc) RetainCount = 1
  2. NewSnowTire_001 (addTire) RetainCount = 2
  3. NewSnowTire_001 (release) RetainCount = 1
  4. NewSnowTire_001 Finally Released by dealloc method.

Best Answer

  1. You should use id (not id*). Objective-C do not have a concept of a root object as you have in for example Java, where java.lang.Object is the root class for any and all classes. Cocoa adds two root classes (classes without a super class) named NSObject, and less common NSProxy. id is a pointer to any object regardless of super class. It is unfortunate that id, and also Class, are defined as a pointers, which means they are the only places where you should not add the '*' character when defining references. An unfortunate legacy from the old days.

  2. Release in main, you should always release objects int he same scope that you create or retain them. The addTire: method is exceptionally god example of this, never release objects that has been handed to you as an argument. Only release objects handed to you as a result (And even then only from the alloc, new and copy method).

  3. The allocation and release of the instance variable tires is a schoolbook example of where it should be done. I would expand the init to check for the super class result, as this though (Never trust super to always work, or even return the same instance):

- (id) init {
    self = [super init];
    if (self) {
      tires = [[NSMutableArray alloc] init];
      NSLog(@"_init: %@", NSStringFromClass([self class]));
    }
    return self;
  }
  1. You can use NSArray if you have access to all four tires from the start. Best way would probably be to require the tires in the init method. If that is not a possibility then you have nothing to gain from using an NSArray over a NSMutableArray.
Related Topic