Where am I leaking memory here? Used Instruments - iphone

Instruments is saying there is a memory leak in this code:
- (void)layoutImageMaskViewForImageAtPath:(NSString *)path withFillColor:(UIColor *)color indexPath:(NSIndexPath *)indexPath {
UIImage *image = [UIImage imageWithContentsOfFile:path];
[self layoutImageMaskViewForImage:image withFillColor:color indexPath:indexPath];
}
UIColor *anIconFillColor = [UIColor colorWithWhite:0.70 alpha:1.0];
NSIndexPath *anIndexPath = [NSIndexPath indexPathForRow:0 inSection:0];
NSString *aPlaceholderPath = [[NSBundle mainBundle] pathForResource:#"path" ofType:#"png"];
[self layoutImageMaskViewForImage:anImage withFillColor:anIconFillColor indexPath:anIndexPath];
and
NSDictionary *anAssignedData = [aReservationData objectForKey:kAssignedSectionKey];
NSMutableArray *anEmployeeTaskQueueList = [NSMutableArray array];
NSArray *anAssignedReservationData = [anAssignedData objectForKey:kEmployeesIdentifier];
for (NSDictionary *aJobQueueData in anAssignedReservationData) {
EmployeeReservationQueue *anAssignedTaskQueue = [[EmployeeReservationQueue alloc] initWithServerDictionary:aJobQueueData];
if (anAssignedTaskQueue.rows.count == 0) {
ReservationTrack *aTrack = [[ReservationTrack alloc] init];
aTrack.rowSortOrder = 0;
aTrack.reservations = [NSArray array];
anAssignedTaskQueue.rows = [NSArray arrayWithObject:aTrack];
[aTrack release];
}
[anEmployeeTaskQueueList addObject:anAssignedTaskQueue];
[anAssignedTaskQueue release];
}

Your second example leaks track. Your last line is releasing aTrack instead.

In second case here:
[aTrack release];
What is aTrack? May be you mean [track release];?
In first case probably that you pass to function non-autoreleased parameters or may be you are not releasing them after calling that method. Just post code where you call for that method and I will check.

Gold memory-management rule in Objective-C :
Each 'init', 'copy','mutableCopy','retain' must call then 'release' or 'autorelease'.

Instruments reports that your app is leaking a ReservationTrack object. By default it shows where the leaked object was allocated, which is the code you posted. The code you posted doesn't leak a ReservationTrack. It stores it in an EmployeeReservationQueue which is stored in an NSMutableArray. One possibility is that you later access the ReservationTrack object, send it retain, and don't send it release or autorelease. Another possibility is that you leak the EmployeeReservationQueue or the NSMutableArray.
If you use the simulator, you can see the full retain/release history of most objects. When a leaked object shows up, mouse over the address of the object and click the right arrow that appears next to the address. Instruments will show you every malloc, retain, release, and autorelease event for that object. If you choose View > Extended Detail from the menu bar, you can click on any of those events and see the stack trace of the event. This should help you track down the unbalanced retain.

Related

Objective-C Memory Management: crash on release

I'm new to Objective-C and can't seem to get the memory management code right. I have the following code:
Media* myMedia = [self.myMediaManager getNextMedia];
self.navigationItem.title = [self.myMediaManager getCategory];
[self.btnImage setImage:myMedia.imageFile forState: UIControlStateNormal];
[self.lblImage setText:myMedia.imageLabel];
//[myMedia release];
My app crashes if I uncomment the above line. Do I need to do something special when I instantiate myMedia?
EDIT:
If myMediaManager is supposed to release it, when would it do that. Here is my code for getNextMedia:
- (Media*) getNextMedia {
DLog(#"Start");
Media* nextMedia = [[Media alloc] init];
[self setNextMediaIndex];
if (self.mediaIndex > -1)
{
nextMedia = [mediaArray objectAtIndex: self.mediaIndex];
}
return nextMedia;
}
EDIT2: I fixed the crashing issue (I was releasing an object I didn't own). I still see leaks and can't seem to find what the issue is.
Only objects that you own can be released.
You can release objects if you new, alloc, copy, mutableCopy or retain them first.
Since there is no alloc/copy/retain in [self.myMediaManager getNextMedia]; you can't release it.
Since myMedia is not retained here, you don't need to release it. When the origin (self.myMediaManager) releases it, it gets destroyed immediately.
NSString *string = [[NSString alloc] init];
[string release]; // now we have to release the string, since we allocated it.
NSString *string = self.navigationItem.title;
// now we don't, since it's a property of `navigationItem` and we didn't retain it.
At this point, since you are just learning, you should probably just start off using ARC with the iOS5 beta versions of XCode. It's good to have an understanding but using ARC will save you many potential pitfalls - by the time you learn enough to produce something iOS5 will be out. You can still build applications targeting iOS4, so you'll still be able to reach a lot of people.
The general rule for memory management is as follows:
For every retain, alloc, copy, or new, you need to call release or autorelease.
Since you did not call any these, you do not need to release myMedia.
For more information, take a look at this other answer I posted that deals with the subject. Also, since you are new to iOS development, I suggest looking at this answer as well.
This updated code is suspicious:
Media* nextMedia = [[Media alloc] init];
[self setNextMediaIndex];
if (self.mediaIndex > -1)
{
nextMedia = [mediaArray objectAtIndex: self.mediaIndex];
}
Depending on the condition in the if() clause, you assign a new value to nextMedia, which makes the value you just allocated unreachable, i.e. it can't be released.
Also, you don't retain the value you get from the array, so you should not release it either. But if the if() clause does not run, you still have the instance you alloc-ed, and that should be released.
That is not good. Try:
Media* nextMedia = [[Media alloc] init];
[self setNextMediaIndex];
if (self.mediaIndex > -1)
{
[nextMedia release];
nextMedia = [[mediaArray objectAtIndex: self.mediaIndex] retain];
}
You could also do (and I would prefer that):
Media *nextMedia;
[self setNextMediaIndex];
if (self.mediaIndex > -1)
{
nextMedia = [[mediaArray objectAtIndex: self.mediaIndex] retain];
}
else
{
nextMedia = [[Media alloc] init];
}
Now you can release nextMedia when that is not needed anymore, without any ambiguity about the retain count.

NSThread, UITableViewCell and Image

I am having some problem with my UITableView. I am downloading the data and setting up the cells like usual, but this time with the NSThread added.
First I will call ASIHTTPRequest in my view did load
Then in - (void)requestFinished:(ASIHTTPRequest *)request I will add the url of the thumbnail into an NSMutableArray
NSString *photoURLString = [NSString stringWithFormat:#"http://some.url.com/img/%#",[thearray objectForKey:#"tn_url"]];
[thumbNailURL addObject:(photoURLString)];
And in the cellForRowAtIndexPath: where I am setting up other data to display, I will call my NSThread
[NSThread detachNewThreadSelector:#selector(loadImage:) toTarget:self withObject:indexPath];
And in my loadImage thread
- (void) loadImage: (NSIndexPath*) indexPath{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString *iconURL = [thumbNailURL objectAtIndex:indexPath.row];
NSURL *url = [NSURL URLWithString:iconURL];
UIImage *icon = [UIImage imageWithData:[NSData dataWithContentsOfURL:url]];
cellImageView = [[UIImageView alloc] initWithFrame:CGRectMake(11.0, 6.0, 61.0, 50.0)];
[cellImageView setImage:icon];
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
[cell.contentView performSelectorOnMainThread:#selector(addSubview:) withObject:cellImageView waitUntilDone:NO];
[pool drain];
}
All the data and image loaded just fine, but if I scroll faster up and down , the application will hang with the following error
[1997:7a1f] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 6 beyond bounds [0 .. 5]' (0x31312d2f 0x345f50e3 0x3127d1b1 0x35aefc1d 0xf4dd 0x333ffb51 0x3347c67b 0x366af589 0x366b2310) terminate called throwing an exceptionProgram received signal: “SIGABRT”.
It would be great if anyone could show me the correct configuration if I am wrong, as I have been trying different approaches for the past 3 days already.
A couple things.
First, I would recommend you consider using something like SDWebImage for async image downloading (looks like Olivier Poitrey is the original author). Github repository here: https://github.com/rs/SDWebImage. I use it for this very purpose and it's great.
That said, I see a few things here.
1) You don't seem to be checking if the cell image is already set... no need to re-download the image if you've already got it. You might get away with this since dataWithContentsOfUrl might offer some caching, but I'm not sure.
2) I can't tell from the code here, but you need to be sure you don't allow the table view to load it's data until after requestFinished is called. Otherwise you'll have a race condition that could cause the type of error you are seeing. Consider setting the tableView's data source in the requestFinished method and then calling reloadData. Otherwise, do as downed suggested and just prefill the URL array with [NSNull null] objects.
Have you tried having the array "thumbNailURL" contain [NSNull null] objects for the number of items that you are trying to display, then replacing them after you load them async, that way you can't be faster than the array.
thumbNailURL = [[NSMutableArray alloc] init];
for (unsigned i = 0; i < [items count]; i++) {
[thumbNailURL addObject:[NSNull null]];
}
Then simply:
[thumbNailURL replaceObjectAtIndex:index withObject:thumbnail];

How to release an object in a forin loop?

I'm new to cocoa / objective-c and i'm struggeling with the releases of my objects. I have the following code:
gastroCategoryList = [[NSMutableArray alloc] init];
for (NSDictionary *gastrocategory in gastrocategories) {
NSString *oid = [gastrocategory objectForKey:#"id"];
GastroCategory *gc = [[GastroCategory alloc] initWithId:[oid intValue] name:[gastrocategory objectForKey:#"name"]];
[gastroCategoryList addObject:gc];
}
The analyzer shows me that the "gastrocategory" defined in the for is a potential memory leak. But i'm not sure if i can release this at the end of the for loop?
Also at the following code:
- (NSArray *)eventsForStage:(int)stageId {
NSMutableArray *result = [[NSMutableArray alloc] init];
for (Event *e in eventList) {
if ([e stageId] == stageId) {
[result addObject:e];
}
}
return result;
}
The Analyzer tells me that my "result" is a potential leak. But where should I release this?
Is there also a simple rule to memorize when i should use assign, copy, retain etc. at the #property ?
Another problem:
- (IBAction)showHungryView:(id)sender {
GastroCategoriesView *gastroCategoriesView = [[GastroCategoriesView alloc] initWithNibName:#"GastroCategoriesView" bundle:nil];
[gastroCategoriesView setDataManager:dataManager];
UIView *currentView = [self view];
UIView *window = [currentView superview];
UIView *gastroView = [gastroCategoriesView view];
[window addSubview:gastroView];
CGRect pageFrame = currentView.frame;
CGFloat pageWidth = pageFrame.size.width;
gastroView.frame = CGRectOffset(pageFrame,pageWidth,0);
[UIView beginAnimations:nil context:NULL];
currentView.frame = CGRectOffset(pageFrame,-pageWidth,0);
gastroView.frame = pageFrame;
[UIView commitAnimations];
//[gastroCategoriesView release];
}
I don't get it, the "gastroCategoriesView" is a potential leak. I tried to release it at the end or with autorelease but neither works fine. Everytime I call the method my app is terminating. Thank you very much again!
In your loop, release each gc after adding it to the list since you won't need it in your loop scope anymore:
gastroCategoryList = [[NSMutableArray alloc] init];
for (NSDictionary *gastrocategory in gastrocategories) {
NSString *oid = [gastrocategory objectForKey:#"id"];
GastroCategory *gc = [[GastroCategory alloc] initWithId:[oid intValue] name:[gastrocategory objectForKey:#"name"]];
[gastroCategoryList addObject:gc];
[gc release];
}
In your method, declare result to be autoreleased to absolve ownership of it from your method:
NSMutableArray *result = [[[NSMutableArray alloc] init] autorelease];
// An alternative to the above, produces an empty autoreleased array
NSMutableArray *result = [NSMutableArray array];
EDIT: in your third issue, you can't release your view controller because its view is being used by the window. Setting it to autorelease also causes the same fate, only delayed.
You'll have to retain your GastroCategoriesView controller somewhere, e.g. in an instance variable of your app delegate.
BoltClock's answer is spot-on as to the first part of your question. I'll try to tackle the rest.
Assign is for simple, non-object types such as int, double, or struct. It generates a setter that does a plain old assignment, as in "foo = newFoo". Copy & retain will, as their names imply, either make a copy of the new value ("foo = [newFoo copy]") or retain it ("foo = [newFoo retain]"). In both cases, the setter will release the old value as appropriate.
So the question is, when to copy and when to retain. The answer is... it depends. How does your class use the new value? Will your class break if some other code modifies the incoming object? Say, for example, you have an NSString* property imaginatively named "theString." Other code can assign an NSMutableString instance to theString - that's legal, because it's an NSString subclass. But that other code might also keep its own reference to the mutable string object, and change its value - is your code prepared to deal with that possibility? If not, it should make its own copy, which the other code can't change.
On the other hand, if your own code makes no assumptions about whether theString might have been changed, and works just as well whether or not it was, then you'd save memory by retaining the incoming object instead of unnecessarily making a copy of it.
Basically, the rule, which is unfortunately not so simple sometimes, is to think carefully about whether your own code needs its own private copy, or can correctly deal with a shared object whose value might be changed by other code.
The reason you can release gc after it is added to the gastroCategoryList is that when an object is added to an array, the array retains that object. So, even though you release your gc, it will still be around; retained by the gastroCategoryList.
When you are returning a newly created object from a method, you need to call autorelease. This will cause the object to be released only after the runtime leaves the scope of the calling method, thereby giving the calling method a chance to do something with the returned value.
Note that if your method starts with the word copy or new, then you should not autorelease your object; you should leave it for the calling method to release.
As for copy vs retain vs assign... as a general rule, copy objects that have a mutable version, such as NSArray, NSSet, NSDictionary, and NSString. This will ensure that the object you have a pointer to is not mutable when you don't want it to be.
Otherwise, use retain whenever you want your class to be ensured that an object is still in memory. This will apply to almost every object except for objects that are considered parents of your object, in which case you would use assign. (See the section on retain cycles here).
Also note that you have to use assign for non-object types such as int.
Read through the Memory Management Programming Guide a bit; it's quite helpful.

Class variable type gets changed

So in my view controller, I run code to populate an NSArray of Customer (custom class) objects. This custom class has objects that are of ANOTHER custom class called Address (a customer has a billing address and a shipping address). In the view controller when a customer in the list is selected, it passes a new view controller a customer object, like so:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
InfoViewController *customerinfoViewController = [[InfoViewController alloc] initWithStyle:UITableViewStyleGrouped andCustomer:[[[customers objectAtIndex:indexPath.section] objectAtIndex:indexPath.row] retain]];
[self.navigationController pushViewController:customerinfoViewController animated:YES];
[customerinfoViewController release];
}
The first time I visit this view controller while running the application, it works fine. However, when I revisit the view controller, something interesting happens. The application crashes, with unrecognized selector sent to instance 0x00whatever. Using the mouseover debugging feature in xCode, I am finding that the first object of the customer's shipAddress variable has its type changed from NSString to NSIndexPath. This does not happen to the customer's billAddress object. Anyone have any idea what is going on here? It seems like I may be having memory management issues but I would definitely like a confirmation on this before I tear my code apart tracking down all the retains and releases....
EDIT: More information here. with the following code, I have an NSMutableArray at the class level. At each iteration of the loop, I am looping through nodes in XML (which works fine). Every time a new letter is detected as the first letter of the name, I create a new subarray and add the customer to it, thus filling my class-level NSMutableArray (customers) with subArrays of customers for each letter of the alphabet detected. My question is about the retains and releases of the cycling customer object. Clang Static says there is an over-retaining error on the customer, but when I fix it according to Clang, the loop crashes. what gives? Related code below:
DDXMLDocument *rootDoc = [[[DDXMLDocument alloc] initWithData:xmlData options:0 error:nil] autorelease];
NSArray *elems = [rootDoc nodesForXPath:#"QBXML/QBXMLMsgsRs/CustomerQueryRs/CustomerRet" error:nil];
DDXMLNode *node;
sectionTitles = [[[NSMutableArray alloc] initWithCapacity:1] retain]; // Letters for UITableView section titles
NSMutableArray *subArray;
NSString *lastchar = #"A";
NSString *testchar;
int indexCount = -1;
customers = [[[NSMutableArray alloc] initWithCapacity:[elems count]] retain];
Customer *newCust;
for (int i = 0; i < [elems count]; i++) {
node = [elems objectAtIndex:i];
newCust = [[Customer alloc] initWithCustomerRetNode:node];
testchar = [[newCust fullName] substringToIndex:1];
if (i == 0 || ![[testchar uppercaseString] isEqualToString:lastchar]) {
[sectionTitles addObject:testchar];
lastchar = testchar;
indexCount++;
subArray = [[NSMutableArray alloc] initWithCapacity:1];
[customers addObject:subArray];
[subArray release];
[[customers lastObject] addObject:[newCust retain]];
}
else {
[[customers lastObject] addObject:[newCust retain]];
}
[newCust release];
}
NOTE: this code works for the most part, but clang doesn't like it.
EDIT: Addresses in the Customer class are assigned like so (which now does not work after Clang fixes)
...
else if ([tempname isEqualToString:#"BillAddress"])
billAddress = [billAddress initWithAddressNode:tempnode];
else if ([tempname isEqualToString:#"ShipAddress"])
shipAddress = [shipAddress initWithAddressNode:tempnode];
...
It sounds like you are having a over release issue, so yes memory management, you might be overreleasing that array you are storing your objects in.Cant really tell from the snippet of code though. Youll have to go and look through the code and find the source. Also using Clang Static Analyzer might be of help to you.

NSMutableArray gets corrupted

I'm doing an iPhone application which uses a navigation control to browse through some data. This data is stored in a sqlite database and I have a class which gets it and returns it in a NSMutableArray of NSStrings.
The problem is that in the first screen of the navigation everything works prefectly, but in the second screen (another view which is pushed) the same code fails because the NSMutableArray gets corrupted. In the debugger I can see that it is returned correctly, but when it's time to use it the pointers have become corrupted and the application crashes.
I have put breakpoints in all my functions, and I can't see anywhere where it can get corrupted. And as the first view, which uses the same exact code, even accesing the same eact tables, works correctly I don't really know where to look.
If anyone want to have a look at the code I have uploaded it to my site: http://sachafuentes.com/iBacus.zip
Thanks in advance.
UPDATE:
The problem lies in the function where I get the data, which looks like (this is a simplified version with some pseudo-code).
-(NSMutableArray *) getPaises {
NSMutableArray * paises;
paises = [[NSMutableArray alloc] init];
while( get new row ) {
NSString *aPais = get the value;
[paises addObject:aPais];
[aPais release];
}
return paises;
}
If I comment out [aPais release] everything works, but to me this looks like a memory leak, as the NSString won't be released.
Okay, here's the problem:
NSString *aPais = [NSString stringWithUTF8String:(char*)sqlite3_column_text(compiledStatement, 0)];
By convention, any time that you see an alloc and an init, you need a release somewhere down the road.
By convention, any time that you use an xWithX: style method, the resulting object will be released for you.
Therefore, this statement:
[aPais release];
will cause your method to crash, as the object is released before it should be. Just remove this line, set your NSMutableArray instance to be autorelease-d and you should get better results.
Look for wrong memory management, that's the likeliest cause for crashes. I think you release your objects somewhere where you shouldn't and therefore you have dangling pointers in the array.
Update1
[aPais release] is wrong, as you don't retain it anywhere in this method. Returned values should always be autoreleased. You should have the same amount of retain as release in your code.
(Some people argue that a retain can also be an alloc init, but I always try to use alloc init autorelease, but this is a matter of style.)
BTW
You should autorelease your array, you're only retaining it here with [[alloc] init].
Any object that you alloc and init must be release-d when you're finished with it, or you will have a memory leak.
Since you are passing the reference outside the scope of the getPaises method, you must autorelease the NSMutableArray, or you will have a memory leak:
paises = [[[NSMutableArray alloc] init] autorelease];
Can you please clarify the step here:
NSString *aPais = get the value;
It's not clear what happens in "get the value" and I suspect this is one cause of instability.
I see that the code is (verbatim)
while(sqlite3_step(compiledStatement) == SQLITE_ROW) {
NSString *aPais = [NSString stringWithUTF8String:
(char*)sqlite3_column_text(compiledStatement, 0)];
[paises addObject:aPais];
[aPais release];
}
...and it's exactly as #gs puts it. aPais is autoreleased and should not be released.
You can also use a convenience constructor to initialize the NSMutableArray:
NSMutableArray *paises = [NSMutableArray array];
This is equivalent to doing:
NSMutableArray *paises = [[[NSMutableArray alloc] init] autorelease];