koan
koan

Reputation: 3686

Autorelease used but still leaking

I am writing a C++ program that has one class that has an Objective-C++ implementation. Each function has an autorelease pool when there is any opportunity for object creation.

When a specific object function gets called more than once I get 3 log messages of the type *** __NSAutoreleaseNoPool(): Object 0x10b730 of class NSCFArray autoreleased with no pool in place - just leaking

There is an autorelease pool in this specific function and also in either of the 2 functions that can call it. Is it possible that one of the frameworks I am using is creating some global objects that get leaked ?

I tried setting a breakpoint on __NSAutoreleaseNoPool but it won't break. I also set NSAutoreleaseHaltOnNoPool and couldn't break either.

EDIT: Here is the code,

qtdata.h :

#ifndef qtdata_h
#define qtdata_h

#include "../videodata.h"

class QTData : public VideoData
{
public:
  QTData();
  ~QTData() { }; 
  bool Open(const char *seqname, QImage *img = NULL);
  bool ReadFirstFrame(const char *seqname, QImage &img);

private:
  bool getFrame(void *handle, QImage *img);
};
#endif

qtdata.mm :

#include <CoreAudio/CoreAudio.h>
#include <QuickTime/Movies.h>
#import <QTKit/QTKit.h>
#include "qtdata.h"

QTData::QTData() : VideoData(){ };

// Open and read first frame into img
bool QTData::Open(const char *seqname, QImage *img)
{
  NSAutoreleasePool *localpool = [[NSAutoreleasePool alloc] init];
  NSError *error;

  QTMovie *movieHandle;
  movieHandle = [[QTMovie movieWithFile:[NSString stringWithCString:seqname
                  encoding:NSUTF8StringEncoding] error:&error] retain];

  if(movieHandle == nil)
  {
    [localpool release];
    return(false);
  }

  [movieHandle gotoBeginning];

  NSSize size = [[movieHandle attributeForKey:QTMovieNaturalSizeAttribute] sizeValue];
  width  = size.width;
  height = size.height;

  bool success = false;
  if(img)
  {
    [movieHandle gotoBeginning];
    success = getFrame(movieHandle, img);
  }

  [localpool drain];
  return(success);
}

bool QTData::getFrame(void *handle, QImage *img)
{
  bool success = false;

  NSAutoreleasePool *localpool = [[NSAutoreleasePool alloc] init];

  NSDictionary *attributes = [NSDictionary dictionaryWithObject:QTMovieFrameImageTypeCVPixelBufferRef
                                           forKey:QTMovieFrameImageType];

  CVPixelBufferRef frame = (CVPixelBufferRef)[(QTMovie *)handle frameImageAtTime:[(QTMovie *)handle currentTime]
                                              withAttributes:attributes error:nil];

  CVPixelBufferLockBaseAddress(frame, 0);       

  QImage *buf;
  int r, g, b;
  char *pdata = (char *)CVPixelBufferGetBaseAddress(frame);
  int stride = CVPixelBufferGetBytesPerRow(frame); 

  buf = new QImage(width, height, QImage::Format_RGB32);

  for(int j = 0; j < height; j++)
  {
    for(int i = 0; i < width; i++)
    {
      r = *(pdata+(i*4)+1);
      g = *(pdata+(i*4)+2);
      b = *(pdata+(i*4)+3);
      buf->setPixel(i, j, qRgb(r, g, b));
    }
    pdata += stride;
  }

  success = true;
  *img = *buf;

  delete buf;

  CVPixelBufferUnlockBaseAddress(frame, 0);
  CVBufferRelease(frame);

  [localpool drain];
  return(success);
}

bool QTData::ReadFirstFrame(const char *seqname, QImage &img)
{
  NSAutoreleasePool *localpool = [[NSAutoreleasePool alloc] init];

  NSError *error = nil;
  QTMovie *movieHandle;
  movieHandle = [[QTMovie movieWithFile:[NSString stringWithCString:seqname
                  encoding:NSUTF8StringEncoding] error:&error] retain];

  if(movieHandle == nil)
  {
    [localpool drain];
    return(false);
  }

  [movieHandle gotoBeginning];
  bool success = getFrame(movieHandle, &img);

  [localpool drain];
  return(success);
}

Upvotes: 0

Views: 505

Answers (1)

Bryan Chen
Bryan Chen

Reputation: 46598

You have to create autorelease pool for each new thread. check main.m, it create it for the main thread.

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// your code
[pool drain];

read NSAutoreleasePool doc

Note: If you are creating secondary threads using the POSIX thread APIs instead of NSThread objects, you cannot use Cocoa, including NSAutoreleasePool, unless Cocoa is in multithreading mode. Cocoa enters multithreading mode only after detaching its first NSThread object. To use Cocoa on secondary POSIX threads, your application must first detach at least one NSThread object, which can immediately exit. You can test whether Cocoa is in multithreading mode with the NSThread class method isMultiThreaded.


update:

So you have to create a NSThread object to enable Cocoa multithreading mode.

NSThread *thread = [[NSThread alloc] init];
[thread start];
[thread release];

Than wrap all of the obj-c code in a autorelease pool like code above.

Upvotes: 2

Related Questions