views:

525

answers:

3

Hi I have a weird problems with NSArray where some of the members of the objects in my array are going out of scope but not the others:

I have a simple object called Section. It has 3 members.

@interface Section : NSObject {
    NSNumber *section_Id;   
    NSNumber *routeId;
    NSString *startLocationName;
}
@property(nonatomic,retain)  NSNumber *section_Id;  
@property(nonatomic,retain)  NSNumber *routeId;
@property(nonatomic,retain)  NSString *startLocationName;
@end


@implementation Section

@synthesize section_Id; 
@synthesize routeId;
@synthesize startLocationName;

//Some static finder methods to get list of Sections from the db
+ (NSMutableArray *) findAllSections:{


- (void)dealloc {
    [section_Id release];
    [routeId release];
    [startLocationName release];

    [super dealloc];
}

@end

I fill it from a database in a method called findAllSection

self.sections = [Section findAllSections];

In find all sections I create some local variables fill them with data from db.

NSNumber *secId = [NSNumber numberWithInt:id_section];
NSNumber *rteId = [NSNumber numberWithInt:id_route];
NSString *startName = @"";

Then create a new Section and store these local variable's data in the Section

Section *section = [[Section alloc] init];

section.section_Id = secId;
section.routeId = rteId;
section.startLocationName = startName;

Then I add the section to the array

[sectionsArray addObject:section];

Then I clean up, releasing local variables and the section I added to the array [secId release]; [rteId release]; [startName release]; [locEnd_name release];

[section release];

In a loop repeat for all Sections (release local variables and section is done in every loop)

The method returns and I check the array and all the Sections are there. I cant seem to dig further down to see the values of the Section objects in the array (is this possible)

Later I try and retrieve one of the Sections

I get it from the array

Section  * section = [self.sections objectAtIndex:row];

Then check the value

NSLog(@" SECTION SELECTED:%@",section.section_Id);

BUT call to section.section_Id crashed as section.section_Id is out of scope.

I check the other members of this Section object and theyre ok. After some trial and error I find that by commenting out the release of the member variable the object is OK.

//[secId release];
[rteId release];
[startName release];
[locEnd_name release];


[section release];

My questions are:

Am I cleaning up ok?

Should I release the object added to an array and the local variable in the function?

Is my dealloc ok in Section?

Does this code look ok and should I be looking elsewhere for the problem?

I'm not doing anything complicated just filling array from DB use it in Table Cell.

If its a stupid mistake then tell me (I did put NEWBIE on the question so dont be shocked it was asked)

I can comment out the release but would prefer to know why or if code looks ok then ill need further digging.

the only place that secId is released is in the dealloc.

Thanks

+4  A: 

You should not be releasing secId, rteId, or startName. secId and rteId are pointers to NSNumber instances created with a factory method that returns an already-autoreleased object. Static strings (i.e. @"") do not need to be released. You need to re-read the Memory Management Programming Guide. Then read it again ;-) It will be your friend.

Barry Wark
@Barry beat me too it. He's right, though. With few exceptions, you only release objects created from `init`, `copy` or `new` methods.
kubi
I would recommend taking anything anyone on SO tells you about memory management very lightly. Go by the memory management rules Barry linked. People have a tendency to restate them informally and often say it slightly wrong. For example, kubi just omitted `copy` and `mutableCopy`. So don't lean too much on restatements of the rules.
Chuck
Barry's right, but just to clarify, you shouldn't be releasing the **local** `NSNumber` instances you're creating. You should still release the instance variables. Generally, you can do this more stylishly by assigning nil to the property (`self.section_Id = nil;`) instead of calling `release` on the instance variable.
warrenm
@warrenm, please do *not* assign `nil` to a property to release the instance variable in your desctructor. Doing so is a recepie for pain if you ever add side-effects to your property setter. The standard practice is to use raw instance variables in the `init` and `dealloc` methods and property access everywhere else.
Barry Wark
I respectfully disagree, unless you're using KVO. In every other situation, and **especially** if you're adding custom behavior to a mutator, you should be double-checking how you perform the eventual release, in which case you can change as necessary. Otherwise, there's no reason to disparage that choice as a matter of style.
warrenm
@warrenm Objective-C is not C++ where destructors get called automatically (and appropriately) for class hierarchies. That's why you have to explicitly call `[super dealloc]` in a subclass `-dealloc` method. In this case, it's impossible for the superclass to know whether a subclass (or a category) has changed the mutator's behavior. @bbum and other Apple developers routinely suggest using direct instance variable access in `-dealloc` (in fact, that's a main reason access to @synthesized instance variables was added in the latest runtime).
Barry Wark
@warrenm Just to clarify, I am not recommending direct access instead of property access everywhere, just in `-init..` and `-dealloc`.
Barry Wark
@Barry_Wark Understood. I think your explanation makes the case for calling `release` within `dealloc`.
warrenm
+3  A: 

You're releasing objects you don't own. You should read the memory management rules.

Chuck
+1  A: 

I'll second (third) the suggestion to read the memory management rules.

The TL;DR version is anything you alloc and call a method with init in the method name on is your responsibility to release. For instance:

NSString *string = [[NSString alloc] initWithFormat:@"%@", someObject];

In this case you must release string. However:

NSString *string = [NSString stringWithFormat:@"%@", someObject];

Here string is autoreleased. It's basically equivalent to this:

NSString *string = [[[NSString alloc] initWithFormat@"%@", someObject] autorelease];

...meaning that the next time through the event loop (which means possibly as soon as your function returns), the system will send a release message to it for you. Apple calls these "convenience methods".

If you have something like this:

NSString *string = @"foo";

Then string is pointing to an instance of NSString that is created by the runtime when your program initializes and won't go out of scope until your program terminates. Never release these either.

Again, read the guidelines and bookmark them. But this should answer your direct question.

Frank Schmitt
Cheers all.//TODO: Friday - R.T.F.M. (Read the Feckin Manual)or should that be R.T.F.MMM (Read the Feckin Mem. Mgt. Manual) ;)
clearbrian