views:

555

answers:

7

Hello everyone! I'm somewhat of a cocoa newbie and I simply can't figure out why I'm getting a spike in the leaks graph in Instruments with this code. It seems to be a small leak (i.e. 16 Bytes and the Leaked Object is "Generalblock-16"; that is the only leaking object and says Self 100%) and it seems to remain that size regardless of whether I choose just 1 file or 12,000 files. I've double-clicked on every line of the Stack Trace shown in the Extended Detail view of Instruments and it doesn't lead me to any line in my code.

Thanks in advance for any help you can give me with this.

Y.V.

P.S.: In case there's nothing wrong with my code (which I doubt) and the leak is simply a bug or something unrelated to my code, is it safe to use this code as it is? Will it bring about instability to my app or make crash or anything like that?

@implementation AppController

-(IBAction)openTheOpenPanel:(id)sender
{
    NSOpenPanel *openThePanel = [NSOpenPanel openPanel];
    [openThePanel setAllowsMultipleSelection:YES];

    if([openThePanel runModal] == NSOKButton)
    {
     NSArray *allTheFiles = [openThePanel filenames]; 

     int theNumberOfFiles = [allTheFiles count];

     int i;

     NSMutableDictionary * theDict;
     theDict = [[NSMutableDictionary alloc] init];

     for (i=0; i < theNumberOfFiles; i++) {
      NSString *thisFile = [allTheFiles objectAtIndex:i];
      NSString *theFileNum = [NSString stringWithFormat:@"%i", i];

      [theDict setObject:thisFile forKey:theFileNum]; 
     }

     [theDict writeToFile:@"/tmp/test_file.txt" atomically:YES];

     [theDict release];
    }
}

@end
+4  A: 

Try running CLang Static Analyzer on your code

http://clang-analyzer.llvm.org/

And fix every single thing it points out. I have never seen it point out something that was wrong, even though sometimes I was sure that it was. It's specifically great at finding leaks and other reference related issues.

Lou Franco
+1  A: 

Hey Yon,

Your code looks good! There aren't any memory leaks in what you've shown. If you want, you can declare theDict like this:

theDict = [[[NSMutableDictionary alloc] init] autorelease];

Calling "autorelease" will add the object to the autorelease pool, and it will be automatically released once your function has executed. It's handy - because you don't have to remember to call release. Either way will work here - though.

Does your app leak each time you call this function, or just the first time? If Instruments doesn't show you a line of code where the leak originates, odds are it's just something in the system. In my experience, a few small leaks happen every so often from random system issues.

EDIT:

A leak of this size shouldn't cause any instability in your app. When you're looking for memory leaks, you want to watch out for:

  1. Leaks involving large chunks of memory (NSData or NSImage objects, etc...)
  2. Leaks inside loops, or in functions called repeatedly (that will add up to be significant).

Even on the iPhone (where your app gets about 28MB of RAM, max), a few leak of 16 bytes or 32 bytes isn't a big deal. Generally, Instruments reports a few leaks right when the app launches - and those aren't a big problem. You just want to make sure you aren't leaking more and more memory as your app runs - because a serious user could keep running your app until all available memory was leaked. The OS won't reuse leaked memory because it thinks your app is still using it - so eventually you'll be unable to allocate memory for new objects.

Hope that helps!

Ben Gotow
+1  A: 

Not that it will solve anything, but I recommend always changing

NSMutableDictionary * theDict;

to

NSMutableDictionary * theDict = nil;

Otherwise theDict wil probably have some weird - and unknown - memory address until you alloc/init it. If you do this:

NSMutableDictionary * theDict;
if (theDict) {
  // condition is true
}

the condition will be met, even though you haven't initialized theDict yourself.

Dave Martorana
A: 

Hey guys! Thanks so much for your prompt answers!

Ben, thank you very much for your suggestion. Autoreleasing the dictionary like you suggested was actually my first approach to the code but it leaked, so I changed my code from autoreleasing to allocating and releasing manually and, unfortunately, it leaks just as much (same object and amount of leak).

In case it is not my code what's causing the leak and -as you mentioned- is just something in the system, do you think it is safe to use my code despite the existence of the slight leak? or will it cause instability or crashes in my app?

I've done extensive testing the way it is and so far it has not shown any problems in any of my tests (I've only noticed the presence of the leak by using instruments).

Thanks again for your help!

A: 

In that code, you have four possible lines that could be responsible for a leak:

  1. NSOpenPanel *openThePanel = [NSOpenPanel openPanel];
  2. [openThePanel setAllowsMultipleSelection:YES];
  3. if([openThePanel runModal] == NSOKButton)
  4. NSArray *allTheFiles = [openThePanel filenames];

Try commenting out each one - mocking up data when you comment out the openPanel call or ask it for filenames, and pretending runModal was called for the if statement.

Comment them out one at a time and test for leaks, then you can see which line is responsible and follow it back...

Kendall Helmstetter Gelner
A: 

valgrind, the king of leak detectors, has been ported to OS X. valgrind will find your memory leak and tell you exactly where the allocation site was, and even more important, what was on the call stack when the object was allocated. valgrind is a lifesafer!

Norman Ramsey
+1  A: 

1) Check to make sure that you don't have NSZombieEnabled set to YES in the executable environment variables .

2) instead of calling :

theDict = [[[NSMutableDictionary alloc] init] autorelease];

You should be able to simply call:

theDict = [NSMutableDictionary dictionary];

They are essentially the same thing.