tags:

views:

54

answers:

3

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.
+2  A: 
  1. (id) or (TireSnow*) is similar, I had problems with understanding this in the beginning too. So basically an object is of a pointer type (kind of), but the id is already a pointer, so you don't need a * after it.

  2. In main. Releasing should happen in the same place as the alloc/retain/copy.

  3. Seems okay to me.

  4. You can use [[NSMutableArray alloc] initWithCapacity:4]. This is only a hint to the array, it will automatically expand if you insert more items. Check [tires length] in the addTire method.


Your -init should look more like this:

-(id)init
{
    if (self = [super init]) {
        // init here
    }
    return self;
}

This allows self to be nil if something breaks in the init-chain.

inkredibl
A: 

The type id is defined like this (in objc.h):

typedef struct objc_object {
    Class isa;
} *id;

So id is already a pointer to an object. An id* would be a pointer to a pointer.

As for where you should release the tire — there's nothing in the code you posted that shows a need to release it at all. That object never claims ownership of the tire, so it has no need to release it. If something claimed ownership of the tire somewhere else in your code, then that object has a responsibility to release its claim when it's finished.

This is explained in the Objective-C memory management rules. It's pretty short and a must-read.

Chuck
+1  A: 
  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.
PeyloW