views:

51

answers:

3

I'm fairly new to Cocoa and Objective-C. Currently I'm developing a fairly basic application to test my knowledge and put some of the stuff I've been reading about into practice. Everything is working, but Leaks reports a number of issues.

None of these leaks seems to be directly applicable to code that I've written (I have read and tried to follow Apple's rules on memory allocation). Currently my project makes use of Garbage Collection and I'm developing on Snow Leopard. Running AnalysisTool finds no issues with my code (aside from a few naming convention warnings).

Currently my application makes use of an NSTableView which I have hooked up to an NSArrayController. Interacting with the NSTableView seems to cause leaks to report issues (actions such as sorting table columns and other standard user interaction). This leads me to believe that my use of the NSArrayController (and my implementation of its content source) is to blame.

Currently the NSArrayController receives its content from an NSMutableArray (timers) handled in my Application's delegate like so:

- (id) init
{
    if (self = [super init])
    {
        timers = [NSMutableArray array];    
    }
    return self;
}
- (void) dealloc
{
    [timers release];
    [super dealloc];
}

Within Interface Builder my NSArrayController has its Object Controller set to the Timing class, which is defined below:

@interface Timing : NSObject {
    NSString *desc;
    NSDate *timestamp;
    bool active;
}
@end

@implementation Timing
-(id) init
{
    if (self = [super init])
    {
        desc = @"New timing";
        timestamp = [[NSDate alloc] init];
        active = false;
    }
    return self;
}
-(void) dealloc
{
    [timestamp release];
    [super dealloc];
}
@end

I've used standard Cocoa bindings to hook up Add and Remove buttons to manipulate the TableView and these seem to work correctly (clicking Add will create a row in the TableView with the value of 'New timing', for instance).

Leaks reports that the libraries responsible are AppKit and CoreGraphics. Although, honestly, I'm still new to the Leaks tool - so I could be reading its output incorrectly. If it helps, I've placed a screenshot of its output here. If anyone could point me in the right direction, that would really be appreciated.

As an aside, I've also been experimenting with manually adding objects to the timers array without the use of Cocoa bindings. Here's what I came up with:

Timing *timingInstance = [[Timing alloc] init];
[timers addObject:timingInstance];
[timingInstance release];

[timersController setContent:timers];
[timersTableView reloadData];

Again, this seems to work, but I thought it best to ask the experts!

+2  A: 

Your memory management for the timers array is not quite correct. Using the array factory method will return an instance of NSMutableArray that has already been autoreleased, so the lifetime of that object is (probably) limited to the end of the current run loop, and it will be over-released when you call release in your dealloc method. The proper way to do it is as follows:

- (id) init
{
    if (self = [super init])
    {
        timers = [[NSMutableArray alloc] initWithCapacity:0];
    }
    return self;
}

This method will give you an instance of NSMutableArray with a retain count of 1, which will then drop to zero (and properly release the memory) when you call release in your dealloc method. The call to alloc in your init method is balanced out by the call to release in your dealloc method. I notice that this is the exact pattern that you used for your NSDate object in the Timing class, so you are already familiar with the idea.

e.James
That's correct, although the code as written will work fine under garbage collection.
Rob Keniger
+1  A: 

Currently my project makes use of Garbage Collection and I'm developing on Snow Leopard

I don't understand. You're using garbage collection right? If so then GC takes care of releasing objects for you so anywhere you use "release" does absolutely nothing. Release calls are ignored when GC is enabled. You only use release when you're managing the memory yourself which is when GC is off. Also, your dealloc methods do nothing too. In GC that method is never used. When you create an object and then you finish with it you tell GC that it's OK to get rid of the object by setting it to nil. That's all you have to do. There is no "release" needed or dealloc method needed. Just set things to nil or or not as needed.

regulus6633
I'd just assumed that ensuring objects were alloc'd and released correctly was considered good practice in the event that I ever wish to turn off Garbage Collection.
ndg
You can still get leaks when using Garbage Collection. If strong references are held to an object that is no longer wanted, the collector will not release that memory. Programming with Garbage Collection turned on is not a magic fix, you still have to be aware of memory issues and use weak references where necessary. Consider a parent-child relationship, if both the parent and the child have a strong reference (ivar) to the other, neither will be collected unless you use the `__weak` keyword on the child's `parent` ivar.
Rob Keniger
+2  A: 

Your code as written is not leaking. The Cocoa frameworks will sometimes generate false leak reports when run under leaks, as certain things such as singletons and caches which are used in the implementation of the frameworks will sometimes show up as leaks even though they're not.

You're probably better off running the ObjectAlloc and/or ObjectGraph instruments to get an idea of when your objects are being allocated and deallocated.

Rob Keniger