3
votes

I have a very basic app: a window with an NSOutlineView bound to an NSTreeController. The outline view displays a simple model object (TCCard). I added two buttons so that I can add and remove model objects from the outline view.

Looking at the app in Instruments (Leaks) I can see that new instances of my model object are created when I add them, but not all are released when I delete them from the outline view. Two or three instances of my model object always remain "living" even when the outline view has no more entries.

Is the outline view or tree controller doing some caching behind the scenes? Code below:

#import "TCAppDelegate.h"

#import "TCCard.h"

@implementation TCAppDelegate

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    TCCard *first = [TCCard new];
    first.title = @"First card";

    // tree controller content is bound to self.cards:
    self.cards = [@[first] mutableCopy]; 
}

- (IBAction)addCard:(id)sender;
{
    TCCard *second = [TCCard new];
    second.title = [NSString stringWithFormat:@"%ld card", self.cards.count];

    [self.treeController addObject:second];
}

- (IBAction)deleteCard:(id)sender;
{
    NSIndexPath *path = [NSIndexPath indexPathWithIndex:self.cards.count - 1];

    [self.treeController setSelectionIndexPath:nil];

    [self.treeController removeObjectAtArrangedObjectIndexPath:path];

    // some model objects continue to live
}

@end

This is a very basic example. In my real app those model objects are quite "heavy" with a lot of references to other objects. I really would like all of them to be released when they are removed from the view.

EDIT: This problem can be reproduced even with Apple's sample code: https://developer.apple.com/library/mac/#samplecode/DragNDropOutlineView/Introduction/Intro.html#//apple_ref/doc/uid/DTS40008831

Run in the example in Instruments and search for SimpleNodeData. Watch the number of instances and then delete all nodes from the example app (via the context menu).

1
Are you using a view-based outline view? If so, cell views are cached, and if those views have bindings to other objects, the objects can be kept alive unexpectedly. You can use the delegate method -outlineView:didRemoveRowView:forRow: to get the cell view for a row and set its objectValue to nil, clearing the bindings and releasing the object.user155959

1 Answers

0
votes

In method - (IBAction)addCard:(id)sender you allocate new TCCard object using [TCCard new], which gives you an object you must explicitly release. Because tree controller retains objects you add to it, you should release it after calling [treeController addObject:]. Like this:

- (IBAction)addCard:(id)sender;
{
    TCCard *second = [TCCard new];
    second.title = [NSString stringWithFormat:@"%ld card", self.cards.count];
    [self.treeController addObject:second];
    [second release];
}

Tip: Use Product > Analyze in Xcode to dedect this kind of bugs in future.