NSImage leaking?

Apprentice
Posts: 13
Joined: 2009.06
Post: #1
So I'm trying to duplicate the SourceView example in my app. I can generate the source list perfectly, fine. I can also expand the containers, and everything is displayed fine. However, when I select an item in the list, the app crashes with an EXC_BAD_ACCESS.

Backtrace:
Code:
#0    0x7fff86532340 in objc_msgSend_vtable14
#1    0x7fff8508f622 in -[NSImage _deallocAuxiliaryStorage]
#2    0x7fff8508f5a1 in -[NSImage dealloc]
*#3    0x100002913 in -[SourceListCell setImage:] at SourceListCell.m:23
*#4    0x100001c31 in -[ProjectController outlineView:willDisplayCell:forTableColumn:item:] at ProjectController.m:166
#5    0x7fff85103085 in -[NSTableView preparedCellAtColumn:row:]
#6    0x7fff8511bc3f in -[NSTableView _drawContentsAtRow:column:withCellFrame:]
#7    0x7fff8511bbb5 in -[NSOutlineView _drawContentsAtRow:column:withCellFrame:]
#8    0x7fff8511acd8 in -[NSTableView drawRow:clipRect:]
#9    0x7fff8511a5cb in -[NSTableView drawRowIndexes:clipRect:]
#10    0x7fff8511a44c in -[NSOutlineView drawRowIndexes:clipRect:]


SourceListCell
Code:
@interface SourceListCell : NSTextFieldCell {
    NSImage *image;
}

@property(nonatomic, retain) NSImage *image;

@end

- (id)init {
    self = [super init];
    
    [self setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]];
    
    return self;
}

- (void)dealloc {
    [image release];
    image = nil;
    [super dealloc];
}

- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView {
    if (image != nil) {
        [image setSize:NSMakeSize(kIconImageSize, kIconImageSize)];
        // the cell has an image: draw the normal item cell
        NSSize imageSize;
        NSRect imageFrame;
        
        imageSize = [image size];
        NSDivideRect(cellFrame, &imageFrame, &cellFrame, 3 + imageSize.width, NSMinXEdge);
        
        imageFrame.origin.x += kImageOriginXOffset;
        imageFrame.origin.y -= kImageOriginYOffset;
        imageFrame.size = imageSize;
        
        if ([controlView isFlipped])
            imageFrame.origin.y += ceil((cellFrame.size.height + imageFrame.size.height) / 2);
        else
            imageFrame.origin.y += ceil((cellFrame.size.height - imageFrame.size.height) / 2);
        [image compositeToPoint:imageFrame.origin operation:NSCompositeSourceOver];
        
        NSRect newFrame = cellFrame;
        newFrame.origin.x += kTextOriginXOffset;
        newFrame.origin.y += kTextOriginYOffset;
        newFrame.size.height -= kTextHeightAdjust;
        [super drawWithFrame:newFrame inView:controlView];
    }
}

Code:
- (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(NSCell*)cell forTableColumn:(NSTableColumn*)tableColumn item:(id)item {
    if ([[tableColumn identifier] isEqualToString:COLUMNID_NAME]) {
            if ([cell isKindOfClass:[SourceListCell class]]) {
                //item = [item representedObject];
                NSImage *iconImage;
                iconImage = [[NSWorkspace sharedWorkspace] iconForFile:[item path]];
                [item setNodeIcon:iconImage];
                
                [(SourceListCell*)cell setImage:[item nodeIcon]];
                
            }
    }
    
}
Quote this message in a reply
⌘-R in Chief
Posts: 1,171
Joined: 2002.05
Post: #2
What's in setNodeIcon: etc?
Quote this message in a reply
Apprentice
Posts: 13
Joined: 2009.06
Post: #3
Nothing, I @synthesize it

@property(nonatomic, retain) NSImage *nodeIcon;
Quote this message in a reply
⌘-R in Chief
Posts: 1,171
Joined: 2002.05
Post: #4
Ah. Looking again: the crash actually has nothing to do with any of the code you posted. Smile

Implement copyWithZone:.
Quote this message in a reply
Apprentice
Posts: 13
Joined: 2009.06
Post: #5
Sweet! Thank you
Quote this message in a reply
⌘-R in Chief
Posts: 1,171
Joined: 2002.05
Post: #6
Do you know why it crashed though?


Cells in table/outline views are constantly being copied by Cocoa for various reasons. The NSCell implementation of copyWithZone: copies all instance variable values. This means that a copy of SourceListCell will have a pointer to the same NSImage instance as the SourceListCell instance it was copied from. Both instances (and potentially many more copies) will eventually try to release that NSImage instance in -dealloc, but every copy never retained the image in the first place.

Thus, all NSCell subclasses with retained objects must implement copyWithZone: to explicitly retain or copy them so they don't get over released.
Quote this message in a reply
Post Reply 

Possibly Related Threads...
Thread: Author Replies: Views: Last Post
  Sending an NSImage on a Network mindwalkernine 1 2,759 Aug 9, 2006 04:23 AM
Last Post: djork
  Animate An NSImage mindwalkernine 1 3,548 Jun 18, 2006 12:30 AM
Last Post: StealthyCoin
  NSImage and NSView problems Joseph Duchesne 1 3,281 Aug 19, 2005 07:36 AM
Last Post: unknown
  Faking a Z Coordinate With NSImage Nick 2 2,559 Jun 11, 2005 05:38 PM
Last Post: Nick
  Drawing: Transparent Pixels and NSImage elhochme 4 5,054 Jun 17, 2003 06:39 PM
Last Post: elhochme