Can't figure out where memory leak comes from - objective-c

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

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.

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.

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:
Leaks involving large chunks of
memory (NSData or NSImage objects, etc...)
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!

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.

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!

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!

In that code, you have four possible lines that could be responsible for a leak:
NSOpenPanel *openThePanel = [NSOpenPanel openPanel];
[openThePanel setAllowsMultipleSelection:YES];
if([openThePanel runModal] == NSOKButton)
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...

Related

Instruments and memory leaks

I'm using the leaks tool in XCode's instruments to find memory leaks (go figure). I have a few leaks each time I run my app at different points in the app. I look at the extended details, and I'm never pointed to any of the code I wrote, only code built into the foundation of xcode. Two examples of this are:
http://imageshack.us/photo/my-images/192/screenshot20110728at102.png/
http://imageshack.us/photo/my-images/853/screenshot20110728at102.png/
As you can see, some of the problems come from the Message UI library. The only place I use that is here:
-(void)displayComposerSheet
{
MFMailComposeViewController *mail = [[MFMailComposeViewController alloc] init];
mail.mailComposeDelegate = self;
[mail setSubject:#"Suggestions"];
[mail setToRecipients:[NSArray arrayWithObjects:#"sampleEmail#gmail.com", nil]];
[self presentModalViewController:mail animated:YES];
[mail release];
}
-(void)launchMailAppOnDevice
{
NSString *recepient = [NSString stringWithFormat:#"mailto:sampleEmail#gmail.com"];
recepient = [recepient stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:recepient]];
}
-(void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error
{
[self dismissModalViewControllerAnimated:YES];
}
How can I fix this? Thanks!
Finding out where a leak comes from is not always an easy task, helping remotely is even more complex.
In your case, what could help is the little arrow that is shown next to the memory address of the leak. If you click on it, you should get shown an information pane with the full stack trace at that moment. Inspect the list of methods presented there and look for some method of yours, then click on it to inspect the code.
There is not much else you can do in Instruments that get an idea about where the leaked object was created. You should then figure out where you could have missed releasing it.
As to Apple SDK, there are a few memory leaks that are reported and that you can find out there on the web, but they are really seldom.
If Instruments does not help you, one important consideration for you is: what was your app doing when Instruments reported the leak? Retrieving data from the web, displaying a new view, whatever... this could help in delimiting the field of your further investigation. If the leaks shows up multiple times, this could also be a great value to identify what part of your program could be producing it.
If you get a clue about that, then go check the code that accomplishes that. One technique (I know that it might sound strange, but try it) is removing/commenting out chunks of code and then checking to see whether the leak is still there.
"Removing/commenting out chunks of code" may mean many things, from not making a web request, to avoid using a class of a type and replacing it with another. This is not always a simple process, because you impair the app functionality, so you have to use your knowledge about how remove functionality and leave your app "testable". If you are lucky, this could help you further delimiting the code that is causing the leak.
Keep in mind that also the static analysis tool could help, and that leaks is not perfect, sometimes it is wrong. There is another technique to discover leaks that is not based on Leaks, but on Object Allocation. This is very powerful in my experience and I strongly encourage you to try that also, although I suspect that it will not help in this case. It is called heapshot analysis.

Why does instruments not find this leak?

Quick one folks. Have a quick look at the code snippet below. I alloc the UILabel *textLabel but I don't release it (commented out). When the method ends, I lose reference to the pointer so it leaks.
Thing is that XCode Instruments doesn't find this leak and I would have thought that it is quite a straightforward case for it to find it. Its not reporting any leaks in my application and yet I found this one myself and its made me a little suspicious.
Am I over looking something or is Instruments rather poor at detecting leaks?
-(UITableViewCell*)newReadOnlyCellWithTitle:(NSString*)title andText:(NSString*)text {
UITableViewCell *cell=[[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil] autorelease];
cell.textLabel.text=title;
cell.selectionStyle=UITableViewCellSelectionStyleNone;
cell.backgroundColor=[UIColor whiteColor];
cell.opaque=YES;
UILabel *textLabel=[[UILabel alloc] initWithFrame:CGRectMake(80, 11, 350, 24)];
textLabel.text=text;
textLabel.textColor=[UIColor lightTextColor];
textLabel.font=[UIFont fontWithName:STANDARD_FONT size:16.0];
textLabel.opaque=YES;
textLabel.backgroundColor=[UIColor clearColor];
[cell.contentView addSubview:textLabel];
//[textLabel release]; //<== without the release this should leak, yep?
return cell;
}
Edit: output from static analyizer...
Instrument's leak detection works by conservatively scanning memory, looking for pointers and building a graph of connections between allocations. If it finds any pointer(s) to an object in memory that can be reached from a global variable or stack variable, then that object cannot be considered leaked.
Instruments does not know the layout or context of a pointer. If you were to malloc(1024) and there happened to be a bunch of pointers in that [recycled] chunk of memory, those would count even though you will never treat those pointers as real references again.
So, no, Leaks can never be 100% accurate. As well, there are far more many ways to leak memory than an actual leak. If you had a global cache, like this:
NSMutableDictionary *myGlobalCache;
And you were to fill that cache but never prune it, that would be an effective memory leak and there is no way that it'll every show up in Instruments.
I wrote up an in depth discussion of Heapshot analysis, which is related and may be of interest.
Let me guess, -newReadOnlyCellWithTitle:andText: gets called by your -tableView:cellForRowAtIndexPath: method when cell == nil?
The thing is, those cells don't get released, they are cached and pulled back by -dequeueReusableCellWithIdentifier:. Table view uses same cells over and over again thus reducing the need to constantly allocate and release memory.
To make sure this is the case, you can try switching to different view controller, then simulating memory warning in simulator. This will force release on previous controller's view and as a result on cells in discussion. You should notice a leak in Instruments.
I think u will see it only when there are no valid references.
So, in your case, there is still a valid reference that goes something like, (probably) Screen -> table -> cell -> label. Looks like there is a possibility that your screen, the primary view, is still in memory, and hence still a valid reference.
You might want to check the objects that are allocated and then not released and still stay in the memory as valid objects. Checking for leaks isn't always enough.
Leaks will only find objects that are not referred to by any other object. For example if you create two objects in a method that refer to each other but no one else refers to either of them they are both technically leaked objects but leaks can see them since they each have an object that refers to them. Static analyzer is pretty good at finding these kinds of things though so the two in concert should kill almost any leak.
Your missing that there are 2 pointers to the object
Locally you have
UILabel *textLabel=[[UILabel alloc] initWithFrame:CGRectMake(80, 11, 350, 24)];
And within the windowing system which will manage it for you (it is my understanding it is not making a copy, but taking ownership)
[cell.contentView addSubview:textLabel];
So release is unnecessary, and with my limited knowledge even bad.
//[textLabel release]; //<== without the release this should leak, yep?

Allocating and releasing memory in iPhone Xcode 3.2.5, iOS 4.2.1

I am having a strange issue with tableview showing (null) when another ViewController comes and goes. I traced it down to the place that it reads the data to display to the tableview and found that it read null for data. To debug this, I need to understand how memory management really works for iPhone. I used the following code in my application delegate to create an array of objects. This was from a sample code which I modified for my app. My question is when one creates an array like this, would the [tempArray release] actually release the array that was just created?
//Initialize the hardware array.
NSMutableArray *tempArray = [[NSMutableArray alloc] init];
self.hardwareArray = tempArray;
[tempArray release];
This type of code seems to be very common for iPhone. The tutorials keeps saying for example, now that you passed the data to the controller you can release it. But my C and C++ experience tells me, if you release the memory it would be gone, and if its address is sitting someplace on the stack, its no good. Is the data being copied to another place rather than address being passed?
Also when releasing memory in a set routine like the one below, should I be checking to see if the modleName is not nil before releasing it. What happens if one released modelName that is already nil?
-(void) setModelName:(NSString *)newValue {
self.isDirty = YES;
[modelName release];
modelName = [newValue copy];
}
My question is when one creates an array like this, would the [tempArray release] actually release the array that was just created?
If the #property of the object is assign it would get released. But usually you use retain or copy as #property. And if the property is retain, the setter you call with self.hardwareArray = ... (ie [self setHardwareArray:...]) will retain the object. If the property is copy it will create a new object that is retained already.
Also when releasing memory in a set routine like the one below, should I be checking to see if the modleName is not nil before releasing it. What happens is one released modelName that is already nil?
In objective-c messages sent to nil are perfectly legal. No need to worry, no need to check for nil.
If you release an instance variable outside of dealloc you should set it to nil after releasing it.
release doesn't do what you think - it isn't equivalent to free() or delete and doesn't cause the memory to be gone.
It really is important to fully understand the few simple memory management rules before you do any coding. It's not difficult, the best resources are the Apple Developer Guides, like this one
http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html%23//apple_ref/doc/uid/10000011i

EXC_BAD_ACCESS for NSMutableArray

I have a NSMutableArray with a few view elements.
I remove the view from the view hierarchy and then check the retain count of the array, it returns 1. I then send [array release] but the code dumps with EXC_BAD_ACCESS.
I see that there are elements in the array but still the code dumps.
Here is what I found during debugging. The array has all the 100 objects present (count on the array returns 100) and the contents when seen on a debugger returns "Out of Scope" for the elements. Also, since the array is in the view hierarchy, the following code reduces the retain count by two:
for (Liv *view1 in viewArray){
NSLog(#"view count = %d", [view1 retainCount]);
[view1 removeFromSuperview];
NSLog(#"view count = %d", [view1 retainCount]);
}
Do not use retainCount
It is useless for this kind of debugging; you are working with views in the framework's view hierarchy. There could be any number of reasons why the retain count goes up or down by 2, 10, or 42.
From the roundabout evidence posted so far, this appears to be a very straightforward memory management issue.
First, use "build and analyze" to have the llvm static analyzer check your code. Fix any problems it identifies.
Next, how is the array allocated? Where do you store it? Post all of the lines of code that declare or manipulate the array.
Finally, as Paul said, turn on zombies and see what happens.
There is an off chance that this isn't a retain/release issue, but there isn't any evidence to indicate that yet.
Did you retain or alloc the array? If not, you shouldn't be sending it a release.
EXC_BAD_ACCESS means you're sending a message to a object that's already been released. You (or some code somewhere) is releasing it prior to the part of your code where you're sending release (or removeAllObjects).
Basically, if your retains and releases are balanced, you won't get this error. We'll need to see some code before we can offer anything more than generic advice.
Using NSLog() and retainCount isn't the easiest way to debug errors of this nature. Assuming you're on Xcode 3.2.6, then try running your code via Run > Run with Performance Tool > Zombies. That should give you a good pointer as to which part of your code is faulty.

Data Formatters temporarily unavailable

I had a problem with my iPad application that is, I need to download more than 10,000 images from a server at a time. I successfully downloaded more than 8000 images, But after that I got an exception like "Program received signal: “0”.
Data Formatters temporarily unavailable, will re-try after a 'continue'. (Unknown error loading shared library "/Developer/usr/lib/libXcodeDebuggerSupport.dylib")
(gdb)" this in the debugger. I tested the memory management. There are no issues in memory management. But still I got the exception. Please help me.
Thanks in advance,
Sekhar Bethalam.
Data formatters are used so that the debugger can represent an object as more than just a pointer or integer value (for example, data formatters allow the debugger to show you the underlying value of NSNumber, or the elements inside an NSArray, etc.). Sometimes—and I'm not sure why—they just stop working. In any case, the fact that the data formatters aren't working is not the root of the issue.
Without seeing any of your code (such as how you download and/or store the images) it is not very easy to diagnose your problem. After some digging around on Google, it appears that programs on iPhone OS receive signal 0 when they are using too many resources (not necessarily just memory). There's nothing you can do to stop iPhone OS from killing your program when it wants to.
A few questions:
Are you trying to download 10,000 images simultaneously or sequentially? Hopefully sequentially!
Even though your there are no detected leaks in your code, your program may still be using too much memory. Are you keeping references to these images in an array or something similar?
Are you in a tight loop? Your code may not keep references to any of the data/URLs/images, but the autorelease pool might be. In tight-loop situations, it is sometimes advisable to create a new autorelease pool at the beginning of the loop, and releasing it at the bottom. For example:
for (NSUInteger i = 0; i < 10000; i++)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// do the work to get the image. Objects are automatically added to the
// most recently created autorelease pool in the current thread.
[pool release];
}
This is my code which I mentioned above,
for (int i=0;i<10000;i++)
{
printf("\n generating images..............:%d",i);
i++;
UIImageView* imageView=[[UIImageView alloc]initWithFrame:CGRectMake(0, 0, spotItem.imageWidth, spotItem.imageHight)];
NSData *receivedData = [NSData dataWithContentsOfURL:[NSURL URLWithString:#"http://sererpath/prudently/iphone/image_s/e545afbf-4e3e-442e-92f9-a7891fc3ea9f/test.png"]];
imageView.image = [[UIImage alloc] initWithData:receivedData] ;
//imageView.image=[UIImage imageNamed:#"1.png"];
[spotItem.subView addSubview:imageView];
[imageView release];
}
I was downloading them directly.