A total Noob here positing for the first time on this forum.
I am writing my very first iPhone app. The code in question is to populate my UITableViewCell in the cellForRowAtIndexPath method.
When I run my app with Leaks Instrument turned on, the leaking part points to this part of my code. I have read about memory management on various books and postings on this site and per my knowledge the code below seems to conform to the required alloc/release coding guidlines. However, the Leaks tool still points to this particular section. I simply don't understand why.
Your help would be appreciated.
ModelClass * myModelData = [myTempDataArray objectAtIndex:[indexPath row]];
NSString * myURL = [[NSString alloc] initWithFormat:#"%#/images/%#", SERVER_URL, [myModelData publisherLogoFileName]];
NSURL * imageURL = [[NSURL alloc] initWithString:myURL];
[myURL release];
[[cell productImageM] setUrl:imageURL];
[imageURL release];
[[self objMan] manage:[cell productImageM]];
return cell;
The Leaks tool doesn't tell you where the object should have been released, it tells you where it was allocated.
That bit of code looks fine; you own myURL long enough to use it to create imageURL and then release it. You give imageURL to the cell's productImageM and then release it. You then do some manipulation on objects you don't own.
At a guess, the type of object returned by [cell productImageM] retains its Url property and subsequently leaks it.
You can try to use the Build & Analyze option from the Build menu. In general it should indicate the exact line of the leak.
Related
I am developing an iPhone application which mainly makes use of the Address Book and database. After fetching about 3000 contacts from the address book, I am attaching string tags to the contacts (5 for each). I am saving my tags in the database.
For Load testing purpose i have added 10,000 tags to the App. But during the load testing of my application, I observed some memory leaks which were not related to the Application code but represents a set of Instruction sets. Also Instruments showed Foundation as the responsible library for the Leak (Extensive use of NSString,NSDictionary,NSArray which belongs to the Foundation framework). My application crashes after 10 - 15 mins of usage.The Crash report mentions, application crashed due to low memory.
Memory profiling using CLANG shows zero leaks. How do i solve these memory leaks?
Are these leaks the real culprit behind the crash? Are there any other tools available to check memory leaks?
I often find my leaks say they're caused by Core Foundation (or any other framework for that matter) but are really my own. With the exception of the Simulator, rarely will you find excessive leaking in the frameworks.
If you open up the detail panel to the right in Instruments you may find listed your App's methods in there. That will give you indication of where it could be coming from in your code. One leak can spring many other leaks, and you may have to find the top level culprit to get rid of the lower level ones.
You should not expect Clang to do anything but find the most obvious leaks. It's very handy, but that's it, just a helpful addition to compiling.
clang is not a leak checker. It only detects a small subset of issues.
For memory leak debugging you should focus on Instruments, specifically the Object Allocation and Leaks instruments. Be sure to understand the difference between leaks and other source of high memory usage though.
Once you've determined that objects are leaking, use Instruments to examine their allocation stack trace (so you can tell what object it is), and their retain/release history.
If it's not a leak, then I suggest investigating the instructions here:http://www.friday.com/bbum/2010/10/17/when-is-a-leak-not-a-leak-using-heapshot-analysis-to-find-undesirable-memory-growth/
Most likely you have code that is creating the foundation objects. Leaks shows you the place of allocation but that is generally due to a call your code made to create the object. You can look at the call chain in Instruments and go back along the call chain until you get to your code - that is the place where you are causing the allocation. Now, for that allocation, look at your memory handling for that object: Do you release it some time later?
There are lots of ways you can fail to release memory property so it would be hard to guess which one you might be hitting. Ones I see when helping people include allocating an object and assigning it to an instance variable via a property with the retain attribute, something like this:
#property (retain) NSString* myString;
...
self.myString = [[NSString alloc] initWithString: #"foo"];
the alloc+init creates a retained object, the self.myString = increments the retain count again. If coded correctly, the dealloc method releases the property via one of:
[myString release];
or
self.myString = nil;
And that takes care of the retain added with the self.myString = but does NOT take care of the retain from creation. Solutions, ONE of the following:
myString = [[NSString alloc] initWithString: #"foo"]; // doesn't call the setter method so no assignment retain - but doesn't call the setter which might be bad if non-trivial setter.
self.myString = [[[NSString alloc] initWithString: #"foo"] autorelease];
autorelease releases the alloc+init retain.
Now of course this is a contrived example because you'd probably really use:
self.myString = [NSString stringWithString: #"foo"];
which is a class method returning an autoreleased string and avoids the problem. But the idea is to show a simple example to explain this type of issue.
There are many other ways to not release memory properly, but the advice to work your way back up the call-chain until you get to your code is the way to go look at where you are triggering the allocation of the memory and then you can figure out why you aren't releasing that properly.
Try to do some issues in u code:
1. Please avoid hide declaration like
NSString *string = [dictionary valueForKey:[dictionary2 valueForKey:#"something"]]
Correct code is:
NSString *key = [dictionary2 valueForKey:#"something"];
NSString *string = [dictionary valueForKey:key];
key = nil;
Try to avoid declaration with autorelease and local declaration like:
NSArray *array = [NSArray array];
If u do this, make sure that u have:
NSArray *array = [NSArray array];
.... some code where array is using;
array = nil;
Better idea is alloc and release immediately when u don't need object and put it to nil.
3. Check if u using correct setters. Probably better idea is avoid to using it, at my experience, deallocate class start leaks for getters and setters, which was using before.
If u post some part of u code, where u seen most leaks (instruments give u possibility to click on leaked objects and see volume of leaks in programming code) community can suggest more.
My code crashes at this function (at the stringByAppendingFormat: with error objc_msgSend() selector name: stringByAppendingFormat).
This is that line:
// imagesPath = ...iPhone Simulator/4.0/Applications/NUMBERS/Documents/images
UIImage *image = [[UIImage alloc] initWithContentsOfFile:[imagesPath stringByAppendingFormat:#"/%d.png", [[self.postsArrayID objectAtIndex:row] intValue]]];
Could it have something to do with the retaining of objects?
Thanks :)
> rootPath =
> [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
> NSUserDomainMask, YES)
> objectAtIndex:0]; imagesPath =
> [rootPath
> stringByAppendingPathComponent:#"/images/"];
Hah! Setting a property and setting
value using self.imagesPath =... fixed
it. Obj-c is so hard to understand
sometimes...
The methods you used to set the get the paths are autoreleased, so when you tried to access them later they had already died. Using the self.imagesPath property will retain the data (you specified it as (nonatomic, retain) - so it will stay around until you release it (or assign anything else using the property accessor self.imagesPath = ....;
Apple's memory management guide is highly recommended, though it is still easy to fail after reading it a couple of times. :-)
Usually a crash in objc_msgSend() implies the message being passed to the object (in this case, stringByAppendingFormat) is not specified for that object. Quick googling reveals that many top pages for stringByAppendingFormat are quite dated, inferring the API has possibly been deprecated in favor of something else.
As a workaround, it would seem +[NSString stringWithFormat:] would be a viable alternative for your use case.
Why not use stringByAppendingPathComponent:? And surely imagesPath is not ........../NUMBERS/images? Would it not be ................/<random ID>/images?
Hey. I have been working on a Twitter application and have been stuck on a EXC_ BAD_ ACCESS error for quite some time. I know that EXC_ BAD_ ACCESS is a memory issue but i cannot pinpoint where the problem is. Here is my code sample:
- (void)viewDidLoad {
[super viewDidLoad];
NSString *path = #"/Volumes/Schools/BHS/Student/740827/Documents/Forrest McIntyre CS193P/Presence2";
NSArray *propList = [NSArray arrayWithContentsOfFile:[NSBundle pathForResource:#"TwitterUsers" ofType:#"plist" inDirectory:path]];
people = [[NSMutableArray alloc]init];
for (NSString *name in propList) {
Person *p = [[Person alloc] initWithUserName: name];
[people addObject: p];
[p release];
}
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
The exception is thrown on the last brace after the comment. I believe that it is truly thrown in the for loop somewhere but just shows up upon exiting.
Here is the implementation file for Person:
#implementation Person
#synthesize image;
#synthesize username;
#synthesize displayName;
#synthesize statusArray;
-(id)initWithUserName:(NSString *)userName {
if(self = [super init])
{
self.username = userName;
NSDictionary *info = [TwitterHelper fetchInfoForUsername:userName];
self.displayName = [info objectForKey:#"name"];
NSLog([NSString stringWithFormat:#"%#",[info objectForKey:#"profile_image_url"]]);
NSString *imageURL2 = [NSString stringWithFormat:#"%#",[info objectForKey:#"profile_image_url"]];
self.image = [UIImage imageWithData: [NSData dataWithContentsOfURL: [NSURL URLWithString: imageURL2]]];
[info release];
self.statusArray = [TwitterHelper fetchTimelineForUsername:userName];
}
return self;
}
#end
Thanks for any help
EDIT: Here is the header file for PersonListViewController (the class that contains the ViewDidLoad).
This is just to show you where people is coming from.
#interface PersonListViewController : UITableViewController {
NSMutableArray *people;
}
#end
since you never retain propList or path you shouldn't be releasing them.
You should, however, release people
For an overview of memory management, see the Memory Management Programming Guide
For quick fixes, try the static analyzer.
I think the problem is here:
[propList release];
Since you created propList using arrayWithContentsOfFile you don't need to release it - it will be automatically released. The autorelease is actually what's causing the error since it is trying to release something that you already released manually.
ETA: as cobbal mentioned, you also don't need to release path.
Debugging EXC_BAD_ACCESS is difficult to debug. This happens when a message is sent to an object that is already released. You need to find out what is causing this generic error by turning on NSZombiEnabled environment variable so the Objective-C environment will be able to 'track' a deallocated object. Using this, when you get the error you can determine where the error occurred by looking at the call stack. You won't know where it is released but at least it will get you close.
I don't have it setup here, but you may also be passing a pointer to the error which will cause the object to not persist as a zombie/dummy.
Bottom line, you need to make sure the variables you are meaning to release, that you retain them as necessary.
This Technical Q&A by Apple gives tips on Finding bugs with EXC_BAD_ACCESS.
For one, neither of these are necessary in your example:
[path release];
[propList release];
because:
path is a string literal (will always exist)
propList is autoreleased
For any EXC_BAD_ACCESS errors, you are usually trying to send a message to a released object. The BEST way to track these down is use NSZombieEnabled.
This works by never actually releasing an object, but by wrapping it up as a "zombie" and setting a flag inside it that says it normally would have been released. This way, if you try to access it again, it still know what it was before you made the error, and with this little bit of information, you can usually backtrack to see what the issue was.
It especially helps in background threads when the Debugger sometimes craps out on any useful information.
VERY IMPORTANT TO NOTE however, is that you need to 100% make sure this is only in your debug code and not your distribution code. Because nothing is ever released, your app will leak and leak and leak. To remind me to do this, I put this log in my appdelegate:
if(getenv("NSZombieEnabled") || getenv("NSAutoreleaseFreedObjectCheckEnabled"))
NSLog(#"NSZombieEnabled/NSAutoreleaseFreedObjectCheckEnabled enabled!");
If you need help finding the exact line, Do a Build-and-Debug (CMD-Y) instead of a Build-and-Run (CMD-R). When the app crashes, the debugger will show you exactly which line and in combination with NSZombieEnabled, you should be able to find out exactly why.
http://www.cocoadev.com/index.pl?NSZombieEnabled can be useful in tracking down EXC_BAD_ACCESS bugs. Instead of deallocating objects when they are released it puts them into a zombie state that raises an exception when they are subsequently accessed. Just be sure not to ever release code with this flag set, as it will leak memory like a sieve.
what is self.editButtonItem? I don't see it in your .h file
A couple of things.
In initWithUserName: you're getting info from a method that doesn't contain alloc/copy/create. Further, you don't explicitly retain it. Yet you release it. This is problematic assuming fetchInfoForUsername: autoreleases its result as expected according to the Cocoa Memory management rules.
Using property accessors in initializers is considered bad form since it can cause KVO notifications to be sent out for a half-baked instance.
I've been staring at this same issue for a long time now, and would really appreciate any help or suggestions. I'm sure its something simple, but I can't seem to find it. In my app delegate I'm loading up a bunch of accessory objects (an object I created, which supports NSCopying) with the following code:
NSString *path = [[NSBundle mainBundle] pathForResource:#"Accessories" ofType:#"plist"];
NSDictionary *accDict = [[NSDictionary alloc] initWithContentsOfFile:path];
self.colors = (NSArray *) [accDict objectForKey:#"Colors"];
self.exteriorAccessories = [self loadAccessoriesForMode:EXTERIOR_MODE withDictionary:accDict];
self.interiorAccessories = [self loadAccessoriesForMode:INTERIOR_MODE withDictionary:accDict];
[accDict release];
And this is the definition for the method its calling:
-(NSArray *)loadAccessoriesForMode:(NSString *)mode withDictionary:(NSDictionary *) dictionary
{
NSMutableArray *tempValues = [[NSMutableArray alloc] init];
for (NSDictionary *value in [dictionary objectForKey:mode])
{
Accessory *accessory = [[Accessory alloc] initWithDictionary:value];
[tempValues addObject:accessory];
[accessory release];
}
NSArray *returnArray = [[NSArray alloc] initWithArray:tempValues copyItems:YES];
[tempValues release];
[returnArray autorelease];
return returnArray;
}
When I get to the release for accDict I'm getting an EXC_BAD_ACCESS exception. If I take out the release of accessory inside the loop, everything is fine - but I'm leaking Accessory objects (which seems obv. to me - if I init it and I alloc it, its my job to release it).
When I step through this in the debugger, I'm seeing the init, copy and dealloc methods all fire on my Accessory object as expected. I can also post code for the Accessory object if you think it will help, but I think the problem is somewhere in this code.
I think I've found the cause, but I'll post it here so others can possibly benefit. It didn't really have anything to do with the code I posted. Rather the problem was inside of the Accessory object. I was setting things directly instead of calling the getters through self.
So this:
value = [dict objectForKey:#"myKey"];
Instead of this:
self.value = [dict objectForKey:#"myKey"];
Somehow this was causing me to have bad side effects on the NSDictionary itself (I thought that was not mutable, but it seems I was somehow messing things up). The only way I found this was to use the very helpful advice that I found on Cocoa With Love.
When I used the Print Description option in XCode, I was able to see that the NSDictionary somehow contained AccessoryValue objects - one of my custom objects that should NOT have been there since this was just loaded from a simple plist. Print Description can be found in XCode by hovering over the object to see its details (while the process is paused in the debugger) and clicking on the little up/down arrows right next to the triangle that expands into object details. For dictionaries, this will dump their entire contents to the console.
Please prefix this with an "I know nothing about objective C but":
It looks to me like you need to release the accessory items after you have copied them into the "returnArray", or maybe not specify "copyItems".
Run Clang on your code. It's a godsend. Clang rules! It will do Static Analysis of your code and tell you what you might be leaking. Great stuff.
I was battling with the Exc_Bad_Access issue for a day and finally found the answer. The issue was when I try to access one of the objects stored in an NSDictionary, the first time was OK but the second access the object turned to be nil even though the object counts in the dictionary remains the same. This strange behavior was due to releasing the object twice. Here is an example:
NSString* nstring=[[[NSString alloc]init]autorelease]
[AnNSDictonaryInstance setObject:nstring forKey:0];
...
[nstring release];
Notice that nstring was set autorelease then release again? It won't show problem right away unit you try to read the dictionary object the second times. I hope one day Apple's development team will be able to flag this as an violation while compiling.
I hope this post will help someone out.
Wayne of Campbell
I'm trying to do a simple thing; read an image from the internet, save it to the app's documents directory on the iphone, and read it back from that file so that i can do other things with it later. Writing the file works fine but when i try to read it back i get an EXC_BAD_ACCESS error in GDB that i have no idea how to resolve. Here is what my code basically looks like:
-(UIImage *) downloadImageToFile {
NSURL * url = [[NSURL alloc] initWithString: self.urlField.text];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
[paths release]
NSString * path = [documentsDirectory stringByAppendingString:#"/testimg.png"];
NSData * data = [[NSData alloc] initWithContentsOfURL:url];
[data writeToFile:path atomically:YES];
return [[UIImage alloc] initWithContentsOfFile:path];
}
The code fails in the return statement when i try to initialize the UIImage from the file. Any ideas?
Edit: neglected to add a release that was the problem in the code originally.
Note: This applies specifically to non-ARC memory management.
Since this has had so many views and the checked answer appropriately states that "code shows a severe lack of knowledge of how memory management works in Objective-C," yet no one has pointed the specific errors out, I figure I'd add an answer that touched on them.
The baseline-level rule that we have to remember about calling methods:
If the method call includes the words alloc, new, copy, or retain, we have ownership of the object created.¹ If we have ownership of an object, it's our responsibility to release it.
If the method call does not contain those words, we do not have ownership of the object created.¹ If we don't have ownership of an object, releasing it is not our responsibility, and we therefore should never do it.
Let's look at each line the OP's code:
-(UIImage *) downloadImageToFile {
We started a new method. In doing so we have started a new context in which each of the created objects lives. Keep this in mind for a bit. The next line:
NSURL * url = [[NSURL alloc] initWithString: self.urlField.text];
We own url: the word alloc there tells us that we have ownership of the object and that we will need to release it ourselves. If we do not then the code will leak memory.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
We do not own paths: no use of the four magic words, so we do not have ownership and must never release it ourselves.
NSString *documentsDirectory = [paths objectAtIndex:0];
We do not own documentsDirectory: no magic words = no ownership.
[paths release]
Going back a couple of lines we see that we don't own paths, so this release will cause an EXC_BAD_ACCESS crash as we try to access something that no longer exists.
NSString * path = [documentsDirectory stringByAppendingString:#"/testimg.png"];
We do not own path: no magic words = no ownership.
NSData * data = [[NSData alloc] initWithContentsOfURL:url];
We own data: the word alloc there tells use that we have ownership of the object and that we will need to release it ourselves. If we do not then the code will leak memory.
The following two lines don't create or release anything. Then comes the last line:
}
The method is over, so the context for the variables has ended. Looking at the code we can see that we owned both url and data, but didn't release either of them. As a result our code will leak memory every time this method is called.
The NSURL object url isn't very big, so it's possible that we might never notice the leak, though it should still be cleaned up, there's no reason to leak it.
The NSData object data is a png image, and could be very large; we are leaking the entire size of the object every time this method is called. Imagine this was called every time a table cell was drawn: it wouldn't take long at all to crash the whole app.
So what do we need to do to fix the problems? It's pretty simple, we simply need to release the objects as soon as we no longer need them, commonly right after the last time they're used:
-(UIImage *) downloadImageToFile {
// We own this object due to the alloc
NSURL * url = [[NSURL alloc] initWithString: self.urlField.text];
// We don't own this object
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
// We don't own this object
NSString *documentsDirectory = [paths objectAtIndex:0];
//[paths release] -- commented out, we don't own paths so can't release it
// We don't own this object
NSString * path = [documentsDirectory stringByAppendingString:#"/testimg.png"];
// We own this object due to the alloc
NSData * data = [[NSData alloc] initWithContentsOfURL:url];
[url release]; //We're done with the url object so we can release it
[data writeToFile:path atomically:YES];
[data release]; //We're done with the data object so we can release it
return [[UIImage alloc] initWithContentsOfFile:path];
//We've released everything we owned so it's safe to leave the context
}
Some people prefer to release everything at once, right before the context closes at the end of the method. In that case both [url release]; and [data release]; would appear right before the closing } brace. I find that if I release them as soon as I can the code is clearer, making it clear when I go over it later exactly where I'm done with objects.
To summarize: we own objects created with alloc, new, copy, or retain in the method calls so must release them before the context ends. We don't own anything else and must never release them.
¹There's nothing actually magical in the four words, they're just a consistently-used reminder by the folks at Apple who created the methods in question. If we create our own initialization or copying methods for a class of our own then the inclusion of the words alloc, new, copy, or retain in its appropriate methods is our responsibility, and if we don't use them in our names then we'll need to remember for ourselves whether ownership has passed.
One thing that helps me a lot is to have a breakpoint on objc_exception_throw. Anytime I'm about to get an exception thrown, I hit this breakpoint and I can debug back up the stack chain. I just leave this breakpoint enabled all the time in my iPhone projects.
To do this, in xcode go to near the bottom of the left pane "Groups & Files" and find "Breakpoints". Open it and click on Project Breakpoints and in the detail pane (top), you'll see a blue field labeled "Double-Click for Symbol." Double-click on it and enter "objc_exception_throw".
Next time you throw an exception, you'll stop and in the debugger, you can walk back up the stack chain to your code that caused the exception.
Your code shows a severe lack of knowledge of how memory management works in Objective-C. In addition to the EXC_BAD_ACCESS errors you're receiving, improper memory management also causes memory leaks which, on a small device like the iPhone, can lead to random crashes.
I recommend you give this a thorogh read:
Introduction to Memory Management Programming Guide for Cocoa
Definitely give memory management rules a quick review. Nothing jumps out that would cause the error you're getting, but you're leaking all those objects your allocating. If you don't understand the retain/release pattern, chances are there's another spot in your code where you're not retaining an object properly, and that's whats causing the EXC_BAD_ACCESS error.
Also note that NSString has methods for dealing with filesystem paths, you should never have to worry about the separator yourself.
In general though, if you're getting EXC_BAD_ACCESSs in your code and you can't for the life of you figure out why, try using NSZombie (no, I'm not kidding).
In Xcode, expand the Executables section on the left. Double click on the listing that has the same name as your project (it should be the only one). In the window that pops up, go to Arguments, and in the bottom portion, click the plus button. The name should be NSZombieEnabled and the value should be set to YES
This way, when you try to access a released object, you'll have a better of what you're doing. Just set the value to NO once you've figured out the bug.
Hope this helps someone!
These errors occur when you are mismanaging memory (ie. an object is being released prematurely or similar)
Try doing something like the following..
UIImage *myImage = [[UIImage alloc] initWithContentsOfFile:path];
return [myImage autorelease];
I spent a lot of time experimenting whilst getting to grips with the concepts of release/autorelease. Sometimes the retain keyword needs to be played also (although probably not in this case)
Another option could be simply the path does not exist, or cannot be read from?
Perhaps, initWithContentsOfFile doesn't take a path argument? Browse around at the different init methods for UIImage, I think there's a different one for accepting a path.
There also might be something fancier you have to do for making a path? I remember doing something with "bundles"? Sorry to be so vague, it's all I remember offhand.
take the slash off the path, and make sure it is in the project. doesn't matter if it is in that dir, but it has to be added to the project for you to access it.