[NSImage initWithData]: Memory leak. Bug in code or in Framework?

  • This simple sample code results in a memory leak in [NSImage
    initWithData:].

    Is it a bug in Cocoa or is it a bug in the code itself?

    Is there a workaround (I already tried multiple things: mutableCopy
    of data to releaseit after initialization, setDataRetained and
    different cache mode for the NSImage).

    No useful pointers found in the list archive or on the net.

    Note that without the distribution notification communication, it
    works fine (this is to illustrate a problem seen in a tool receiving
    dist notifications).

    -----------------------------------------------------

    #import "ILeakThereforeIam.h"

    @implementation ILeakThereforeIam

    - (void) awakeFromNib
    {
    retainedImageData_=[[[NSImage imageNamed:@"NSApplicationIcon"]
    TIFFRepresentation] retain];

    [[NSDistributedNotificationCenter defaultCenter] addObserver:self
    selector:@selector(notificationDidReceive:) name:@"DATA" object:nil
    suspensionBehavior:NSNotificationSuspensionBehaviorDeliverImmediately];

    [[NSTimer scheduledTimerWithTimeInterval:0.5f
                                         target:self
            selector:@selector(periodicTask:)
            userInfo:nil
                                        repeats:YES] retain];
    }

    - (void) notificationDidReceive:(NSNotification *) inNotification
    {
    NSDictionary * tUserInfo;

    NSLog(@"receive notification");

    tUserInfo=[inNotification userInfo];

    if (tUserInfo!=nil)
    {
      NSData * tData;

      tData=[tUserInfo objectForKey:@"DATA"];

      if (tData!=nil)
      {
      NSImage * tImage;

      NSLog(@"Do you leak it?");

      tImage=[[NSImage alloc] initWithData:tData];

      if (tImage!=nil)
      {
        NSLog(@"Yes I do, see Activity Monitor");

        [tImage release];
      }
      }
    }
    }

    - (void) periodicTask:(NSTimer *) inTimer
    {
    NSDistributedNotificationCenter * tNotificationCenter;

    tNotificationCenter=[NSDistributedNotificationCenter defaultCenter];

    if (tNotificationCenter!=nil)
    {
      NSData * tData;

      [tNotificationCenter postNotificationName:@"DATA" object:nil
    userInfo:[NSDictionary
    dictionaryWithObjectsAndKeys:retainedImageData_,@"DATA",nil]
    deliverImmediately:YES];
    }
    }

    @end
  • On Dec 13, 2007, at 9:04 PM, Stéphane wrote:

    > This simple sample code results in a memory leak in [NSImage
    > initWithData:].
    >
    > Is it a bug in Cocoa or is it a bug in the code itself?
    >
    > Is there a workaround (I already tried multiple things: mutableCopy
    > of data to releaseit after initialization, setDataRetained and
    > different cache mode for the NSImage).
    >
    > No useful pointers found in the list archive or on the net.
    >
    >
    >
    > Note that without the distribution notification communication, it
    > works fine (this is to illustrate a problem seen in a tool
    > receiving dist notifications).

    It looks like this is a bug in [NSBitmapImageRep imageRepsWithData:]
    (apparently the code used by initWithData if I am to believe the
    Gnustep source code) or one of its method when invoked in this mode.

    If I delay the initWithData: code with a
    performSelector:withObject:afterDelay:, it does not leak.

    A re-reading of the NSDistributedNotificationCenter does not
    highlight any obvious reason for this leak to happen.
  • BTW, I don't think there is any evidence to support the idea that
    GNUstep's implementation has any similarities with Cocoa at this point.
    Same (well, similar) APIs, totally unique codebases.

    Stephane Sudre wrote:
    >
    > On Dec 13, 2007, at 9:04 PM, Stéphane wrote:
    >
    >> This simple sample code results in a memory leak in [NSImage
    >> initWithData:].
    >>
    >> Is it a bug in Cocoa or is it a bug in the code itself?
    >>
    >> Is there a workaround (I already tried multiple things: mutableCopy
    >> of data to releaseit after initialization, setDataRetained and
    >> different cache mode for the NSImage).
    >>
    >> No useful pointers found in the list archive or on the net.
    >>
    >>
    >>
    >> Note that without the distribution notification communication, it
    >> works fine (this is to illustrate a problem seen in a tool receiving
    >> dist notifications).
    >
    > It looks like this is a bug in [NSBitmapImageRep imageRepsWithData:]
    > (apparently the code used by initWithData if I am to believe the
    > Gnustep source code) or one of its method when invoked in this mode.
    >
    > If I delay the initWithData: code with a
    > performSelector:withObject:afterDelay:, it does not leak.
    >
    > A re-reading of the NSDistributedNotificationCenter does not highlight
    > any obvious reason for this leak to happen.
  • On Dec 13, 2007, at 2:59 PM, Stephane Sudre wrote:

    >
    > On Dec 13, 2007, at 9:04 PM, Stéphane wrote:
    >
    >> This simple sample code results in a memory leak in [NSImage
    >> initWithData:].
    >>
    >> Is it a bug in Cocoa or is it a bug in the code itself?
    >>
    >> Is there a workaround (I already tried multiple things:
    >> mutableCopy of data to releaseit after initialization,
    >> setDataRetained and different cache mode for the NSImage).
    >>
    >> No useful pointers found in the list archive or on the net.
    >>
    >>
    >>
    >> Note that without the distribution notification communication, it
    >> works fine (this is to illustrate a problem seen in a tool
    >> receiving dist notifications).
    >
    > It looks like this is a bug in [NSBitmapImageRep
    > imageRepsWithData:] (apparently the code used by initWithData if I
    > am to believe the Gnustep source code) or one of its method when
    > invoked in this mode.
    >
    > If I delay the initWithData: code with a
    > performSelector:withObject:afterDelay:, it does not leak.
    >
    > A re-reading of the NSDistributedNotificationCenter does not
    > highlight any obvious reason for this leak to happen.

    Hi Stephane,

    What's going on here is that the autoreleased objects in the
    distributed notification callback are being collected by the
    outermost autorelease pool, which is only drained when an event is
    received.  If you add an autorelease pool to your callback, the
    "leak" disappears.  Also, if you just do something in the app - say,
    open a menu - all the "leaked" memory is reclaimed.

    So this isn't strictly a leak, but it's worth a bug report anyways.
    Please file one.  Meanwhile, adding an autorelease pool to your
    notification callback is the best workaround.

    Thanks!
    -Peter
  • On Dec 14, 2007, at 3:30 AM, Peter Ammon wrote:

    >
    > On Dec 13, 2007, at 2:59 PM, Stephane Sudre wrote:
    >
    >>
    >> On Dec 13, 2007, at 9:04 PM, Stéphane wrote:
    >>
    >>> This simple sample code results in a memory leak in [NSImage
    >>> initWithData:].
    >>>
    >>> Is it a bug in Cocoa or is it a bug in the code itself?
    >>>
    >>> Is there a workaround (I already tried multiple things:
    >>> mutableCopy of data to releaseit after initialization,
    >>> setDataRetained and different cache mode for the NSImage).
    >>>
    >>> No useful pointers found in the list archive or on the net.
    >>>
    >>>
    >>>
    >>> Note that without the distribution notification communication, it
    >>> works fine (this is to illustrate a problem seen in a tool
    >>> receiving dist notifications).
    >>
    >> It looks like this is a bug in [NSBitmapImageRep
    >> imageRepsWithData:] (apparently the code used by initWithData if I
    >> am to believe the Gnustep source code) or one of its method when
    >> invoked in this mode.
    >>
    >> If I delay the initWithData: code with a
    >> performSelector:withObject:afterDelay:, it does not leak.
    >>
    >> A re-reading of the NSDistributedNotificationCenter does not
    >> highlight any obvious reason for this leak to happen.
    >
    > Hi Stephane,
    >
    > What's going on here is that the autoreleased objects in the
    > distributed notification callback are being collected by the
    > outermost autorelease pool, which is only drained when an event is
    > received.  If you add an autorelease pool to your callback, the
    > "leak" disappears.  Also, if you just do something in the app -
    > say, open a menu - all the "leaked" memory is reclaimed.
    >
    > So this isn't strictly a leak, but it's worth a bug report
    > anyways.  Please file one.  Meanwhile, adding an autorelease pool
    > to your notification callback is the best workaround.

    OK, thanks. It indeeds solve this case. It's an interesting fact to
    take into account when working on a tool with no UI.

    I will file a bug or enhancement request.
previous month december 2007 next month
MTWTFSS
          1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31            
Go to today