NSDictionary leak on iPhone and object reference graph - iphone

I am simply loading a plist into a temporary dictionary for initializing my data model. Unfortunately this single line of code below results in consistent leak throughout the app life cycle as per Instruments. The leaked objects being NSCFString and GeneralBlock on a Malloc and the responsible library being Foundations.
My two questions for the experienced eyes:
Am I doing something strange to trigger this ? I tried surrounding it in autorelease block but it had no effect.
Is there a way to print list of object references of a suspect object to get insight into the object orphaning process.
Leaking Line:
NSDictionary *tempDict = [NSDictionary dictionaryWithContentsOfFile:
[[NSBundle mainBundle]
pathForResource:resourceName
ofType:#"plist"]];
totalChapters = [[tempDict objectForKey:#"NumberOfChapters"] intValue];
chapterList = [[NSMutableArray alloc] initWithCapacity: totalChapters];
[chapterList addObjectsFromArray:[tempDict objectForKey:#"Chapters"]];

It appears you may be leaking at this line:
[[NSMutableArray alloc] initWithCapacity: totalChapters];
If that object isn't being released, then any objects you add to it won't be released either
Edit (because it's too long for a comment):
Instruments tells you where the memory was allocated, but not why it is still being retained. When NSDictionary loads the contents of a file, it has to create an object for each element it loads. If one later retrieves an object using objectForKey:, retain it and forget to release, a leak will be reported. The dictionaryWithContentsOfFile statement will be blamed for it because it performed the allocation.
I concur with Don's phantom debugging. Most likely you haven't released the old chapterList when you assign to it the second time.

I am releasing all the allocated stuff in the dealloc method of this class. The TROUBLED line below gets repeatedly called in each alloc init to help me load new chapters based on user selection.
NSDictionary *tempDict = [NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:resourceName ofType:#"plist"]];
Since I am using convenience method on NSDictionary I don't have anything to Dealloc. But Instrument points to this particular line as the source of leak via a Malloc.
tempDict is a just a scratch space for a dictionary to load a plist and init the ivars.

The autorelease block only effects objects placed into the autorelease pool. So tempDict is place in the autorelease pool but totalChapters is not. If you would like to use the autorelease pool then you either want:
[[[NSMutableArray alloc] initWithCapacity: totalChapters] autorelease];
or don't use the autorelease pool and use:
[[NSMutableArray alloc] initWithCapacity: totalChapters]
and then
[totalChapters release]
Avoiding the autorelease pool is recommended unless necessary.

Related

Understanding memory management in ios

I am in the process of learnig objective-c and programming an iPad app. One thing I keep tripping myself up on and having to re-read is memory management. I am getting there...slowly. Basic rules such as for every alloc / retain you must have a release is useful. However, one relatively basic thing eludes me and I wonder if someone could explain...
Take the following code...
NSArray *myArray = [[NSArray alloc] init];
myArray = [someNSSet allObjects];
This is relatively straight forward coding and would require a [myArray release] statement.
However, I keep seeing examples of (and indeed, I have used extensively the following 'short cut'...
NSArray *myArray = (NSArray *)[someNSSet allObjects];
How, as far as I understand when you use the (NSString *) you dont need to use a [myArray release] statement, but I dont understand why.
Could someone possible explain?
NSArray *myArray = [[NSArray alloc] init];
myArray = [someNSSet allObjects];
this code is leaking myArray because you lose the reference to NSArray that you've allocated on the first line; you don't need to alloc here, because on the second line you're assigning a new value to myArray.
NSArray *myArray = (NSArray *)[someNSSet allObjects];
and this code example is perfectly fine, you're assigning the result of [someNSSet allObjects] to myArray pointer and you don't own the returned value, so you don't need to care about releasing it.
Consider using ARC (Automatic Retain Counting) for you project. With ARC the compiler takes care of retain counts so you don't have to, in fact aren't allowed to. There is a refactoring that will convert a current project.
As you said, there is a leak in the first code you posted. so you must add a release:
NSArray *myArray = [[NSArray alloc] init];
[myArray release];
myArray = [someNSSet allObjects];
In fact, when you obtain an object through a method that starts with alloc, new or copy, you own it, and you should release it. That's why, here you should release the array you obtained using the method alloc. This convention makes it easy to know when you own objects and when you don't. So remember: alloc, new or copy.
As for the second example, you obtained the array though a method that doesn't start with one of the three words (alloc, new or copy), so you don't own the object, and you are not responsible of releasing it. In fact, the array you obtained is an autoreleased object, which means that even though its retain count is currently 1, it will be automatically released when something called the autorelease pool is drained.
Here is a reference about Memory Management Rules.
In the first line:
NSArray *myArray = [[NSArray alloc] init]
some amount of memory is allocated for an array (actually in this case it is senseless since the size of the array is 0. Keep in mind that NSArray is immutable!).
The variable myArray holds the address of the first byte of the reserved memory area.
Now in the second line you change the value of myArray which now will point to the first byte of the memory area where [someNSSet allObjects] is stored. At this moment you do not know any more where the array is stored what you've created in the first line. And so you have a leak.
The line:
NSArray *myArray = (NSArray *)[someNSSet allObjects];
is correct, since you do not reserve any memory at this point. If you are not using ARC you might call retain in order to keep GC away from the referenced block of memory. In other way you might receive a BAD_EXEC when the owner of the object releases it and you try to access it through e.g.: [myArray objectAtIndex:0]

What is the right way to get an NSString object from an NSArray, keeping Memory Managment in mind?

What is the right way to get an NSString object from an NSArray, keeping Memory Managment in mind.
Suppose I have an array
NSArray *myNewArray = [[NSArray alloc] initWithObjects:.......];
Now I want to get an object from this NSArray at index 2.
NSString *nameString = [myNewArray objectAtIndex:2]; // is it the right way? how to deal with "nameString"
// now regarding memory managment, should I release it ?
OR I should first alloc nameString and then assign value to it ?
Check out the Cocoa Memory Management rules (how many times have I started a post with that sentence?). In particular
You only release or autorelease objects you own.
You take ownership of an object if you create it using a method whose name begins with “alloc”, “new”, “copy”, or “mutableCopy” (for example, alloc, newObject, or mutableCopy), or if you send it a retain message.
You use release or autorelease to relinquish ownership of an object. autorelease just means “send a release message in the future” (specifically: when the used autorelease pool receives a drain message—to understand when this will be, see “Autorelease Pools”).
Does the method objectAtIndex: begin with "alloc"? No. Does it begin with "new"? No. Does it begin with "copy" or "mutableCopy"? No. Have you sent retain to the returned object? No.
Therefore you do not own nameString. Therefore you must not release or auto release it.
Sorry if the above seems a bit "leading by the nose" but when I first started with Objective-C, I found it useful to pretty much go through all the above in my head in exactly that way, otherwise I tended to get it wrong. It doesn't take long for it all to become second nature.
I think you don't totally get the "pointer" concept yet. Your variable nameString is just a pointer. Not a string.
In the line :
NSString *nameString = [myNewArray objectAtIndex:2];
You just assign the pointer to the actual memory address of the second object of the array. That's all.
If you are about to keep this object alive (even if the array will be deallocted), you better retain that object.
NSArray *myNewArray = [[NSArray alloc] initWithObjects:.......];
NSString *nameString = [myNewArray objectAtIndex:2]
[myArray release];
That's it, no need to bother any more with it. NSString will be released by environment itself.
There is no need to allocate or initialize NSString object and after all if you are not allocating any memory then there is no need to release....Only release NSArray objects nothing else....
You are going through right way...
There is no need to release the nameString.You only release when you are using(alloc”, “new”, “copy”, or “mutableCopy”) .
NSArray *myNewArray = [[NSArray alloc] initWithObjects:.......];
and getting value in NSString from any of index wont affect memory ...no need to allocate here.
The collected NSString need not required to be retained until you are passing its ownership to another controller/object.

Need to retain an NSArray twice to avoid a crash - why?

I have a complex App that runs reliably, but I'm puzzled why I need to retain a particular NSArray ('sources') twice to prevent a crash (although no exception is reported on the console, but the application crashes and returns to the springboard).
A snippet of the code is included below. There's too much code to paste it all, but you have my word that there are no explicit calls to release the array. 'sources' is an instance variable.
If I only retain the array once (or not at all), I get the crash. With two retains, the App is perfectly stable.
NSString *plistPath = [[NSBundle mainBundle] pathForResource:#"Sources" ofType:#"plist"];
sources = [[NSArray arrayWithContentsOfFile:plistPath] autorelease];
[sources retain];
[sources retain];
Thoughts on why I would need to retain this array twice appreciated. Thanks in advance.
You’re using +arrayWithContentsOfFile:, which returns an autoreleased object, then autoreleasing it again. Take out the call to -autorelease and you’ll be OK. You could re-write it as so:
sources  = [[NSArray arrayWithContentsOfFile:plistPath] retain];
There is an explicit call to release the array. autorelease is just as explicit as release — it just happens later. Not only that, but it was wrong to autorelease the array in the first place, since you didn't own it. One retain is necessary to claim ownership of the array. The second one prevents the crash by balancing out the incorrect use of autorelease.
Is it something to do with that autorelease? I can't see why that's there: it should be the factory method that autoreleases. Although I don't know what the consequence of adding an extra autorelease is, it might be worth seeing what happens if you take that out along with one of the retains.
Ditch the autorelease on the factory method. It's why you need a second retain.
That's because arrayWithContentsOfFile: returns an autoreleased array to you. Calling autorelease on this array will release it twice at the end of current event run loop.
NSString *plistPath = [[NSBundle mainBundle] pathForResource:#"Sources" ofType:#"plist"];
sources = [[NSArray alloc] initWithContentsOfFile:plistPath];

iPhone memory management, a newbie question

I've seen in (Apple) sample code two types of ways of allocation memory, and am not sure I understand the difference and resulting behavior.
// FAILS
NSMutableArray *anArray = [NSMutableArray array];
[anArray release];
// WORKS
NSMutableArray *anArray1 = [[NSMutableArray alloc] init];
[anArray release];
By "FAILS" I mean I get crashes/runtime warnings etc., and not always as soon as I call the release...
Any explanation appreciated.
Thanks
Please keep in mind that
NSMutableArray *anArray = [NSMutableArray array];
acts like:
NSMutableArray *anArray1 = [[[NSMutableArray alloc] init] autorelease];
So doing a release again will cause the crash as you are trying to release an autoreleased object.
Hope this helps you.
Thanks,
Madhup
In the first instance you are getting an autoreleased object, which you don't need to release
The second instance is where you are manually allocating the memory yourself, so you a responsible for releasing it.
Read this documentation for help:-
http://developer.apple.com/mac/library/documentation/cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html
To clarify on djhworlds response:
alloc, copy, mutableCopy and new make you the owner of the new object, retain makes you an owner of an existing object, and you become responsible for -[(auto)release]ing it. Other methods return an object that has been -[autoreleased], and thus you don't have any responsibility for it, but beware: It will disappear on the next iteration of the run loop (usually), as that is generally when the autorelease pool drains.
The practical upshot of this is that the //FAILS version works perfectly in the context of that particular piece of code, but once the run loop rolls around and the pool is drained, your object, being already released and gone, causes things to go boom.

retain will allocate Memory in RAM?

the follwing will allocate memory in RAM ....?
NSArray *obj = [[NSArray arrayWithObjects:
#"Hai", #"iHow",
nil] retain];
Yes. It will create an NSArray object and store it on the heap. The arrayWithObject method returns an autoreleased object, but your extra retain statement makes sure the reference count will be at least one, and the memory won't get freed until you explicitly release it.
It might be worth adding, it's not the "retain" statement that allocates the memory, the memory is allocated by the arrayWithObject method. The retain statement simply increments the reference count for that object.
To add to Tom's correct answer, the line:
[NSArray arrayWithObjects: ...]
is equivalent to:
[[[NSArray alloc] initWithObjects:...] autorelease]
so rather than tacking a retain onto the first line, you could just do:
[[NSArray alloc] initWithObjects:...]
In any case, the memory is allocated in alloc, whether that method appears in your code or is implicit (as it is in autoreleasing class-method convenience calls like the first one).