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.
Related
I have a UIViewController that has :
#property UIImagePickerController* mainPicker;
and with a button, I'm presenting that mainPicker like :
- (IBAction)takePhoto:(UIButton *)sender
{
// Take a photo.
if(![UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera])
{
// No camera is available, show an alert.
UIAlertView* newAlert = [[UIAlertView alloc] initWithTitle:#"Warning"
message:#"Camera is not available."
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[newAlert show];
return;
}
if(mainPicker == nil)
{
mainPicker = [[UIImagePickerController alloc]init];
mainPicker.delegate = self;
mainPicker.allowsEditing = YES; //I've tried without this line, didn't affect at all.
mainPicker.sourceType = UIImagePickerControllerSourceTypeCamera;
}
[self presentViewController:mainPicker animated:YES completion:nil];
}
The first problem is ;
Snapshotting a view that has not been rendered results in an empty snapshot.
Ensure your view has been rendered at least once before snapshotting or snapshot after screen updates.
Also, whenever that view controller is presented, there are at least two memory warnings.
After I take a photo, it gets worse. It's literally spamming "Received memory warning.".
Here is an Instrument screenshot, hope it would help.
The memory is about 4 MB at the beginning. After taking a photo, it goes up to 10 MB. While dismissing, I'm saving the UIImage, so it's nearly 30 MB after the dismissal.
(That peak of the memory is probably caused by writeToFile:. Also, that leak there is about 600 bytes only).
Currently, I'm testing on iPhone 5S, with iOS 7.
I've tried enabling zombies, dispatching the picker after a while, allowing/disallowing editing, etc. None of them worked. Also, I'm not trying to present picker view instantly after loading the view controller.
Additional note, I've used the functions in the answer, and here is the logs;
Memory used 9588.7 (+9589), free 32063.5 kb
Memory used 10281.0 ( +692), free 18448.4 kb
Watching memory usage in iOS
Isn't it a bit weird to see 32 MB free memory in the device, whilst Instruments is telling another story?
Here are a few explanations to help you solve your problem.
First of all, the Zombies diagnostic tool is meant to debug crashes in which memory that was already freed is being accessed. This doesn't seem to be your problem here and thus the Zombies tool will be useless to you for this particular problem.
Second, the screenshot you have provided us is showing the Leaks instruments. The elements you see in that list are objects that your program has allocated and forgotten about without releasing them beforehand. This means that you do not have any single remaining pointer towards that memory that Instruments knows about. Fixing these leaks is a first step towards fixing your memory warnings.
Third, fixing your leaks probably won't be enough to fix your memory warning problems. These warnings indicate that you are using too much memory to iOS's liking. Considering your leaks account for a mere 600 bytes, the problem seems to be your abandoned memory. Abandoned memory is memory that you have allocated, and towards which you still have live references even though they probably won't ever be used again by your application.
In order to help you in fixing your problems, here is some relevant documentation to fix both memory leaks and abandoned memory using Instruments :
https://developer.apple.com/library/mac/recipes/Instruments_help_articles/FindingMemoryLeaksinYourApp/FindingMemoryLeaksinYourApp.html#//apple_ref/doc/uid/TP40012965-CH32-SW1
https://developer.apple.com/library/mac/recipes/Instruments_help_articles/FindingAbandonedMemory/FindingAbandonedMemory.html
Also, here is a useful blog post about abandoned memory :
http://www.friday.com/bbum/2010/10/17/when-is-a-leak-not-a-leak-using-heapshot-analysis-to-find-undesirable-memory-growth/
I'm a newbie trying to figure out a memory leak problem. Does anyone see anything wrong with the following code?
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self.navigationController.navigationBar setTintColor:[UIColor colorWithRed:0.10 green:.20 blue:0.30 alpha:1]];
}
In case it helps, Instruments shows the leaked block with the following info...
Event Type: Malloc
Responsible Library: CoreGraphics
Responsible Caller: CGTypeCreateInstanceWithAllocator
Any help would be greatly appreciated. (I'm still very new, so please explain in as much detail as possible.)
Much thanks!
Bit of a long shot but I'm not sure if it was just a bug in my project or not but I had a similar issue once when setting tintColor in viewWillAppear. I ended up adding an nil check before assigning the color again and this cleared it up for me (I didn't do any further investigation into it so I could be wrong).
if (nil == self.navigationController.navigationBar.tintColor) {
self.navigationController.navigationBar = [UIColor colorWithRed:0.10 green:.20 blue:0.30 alpha:1];
}
The code you have posted is correct, as far as memory management is concerned.
Instruments Leaks will simply show the point where the leaked object is allocated, not the point where it is actually leaked.
So, your leak is elsewhere. Looking at your code, I would say that the leak is possibly occurring when you release the class containing that code, or elsewhere along the dynamical path to dealloc.
Try commenting out setTintColor to check whether the leak is still there.
Hope this explanation can put you on the right track, but in any case, if you need more help, you should post more code...
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?
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.
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...