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

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];

Related

Why UPDATE statement works with initWithFormat and NOT with stringWithFormat?

I was having an issue with my UPDATE statement as I was telling here: Update issue with sqliteManager
I found out that initWithFormat WORKS
NSString *sqlStr = [[NSString alloc] initWithFormat:#"UPDATE User SET Name = :Name WHERE Id = :Id"];
BUT not stringWithFormat:
NSString* sqlStr = [NSString stringWithFormat:#"UPDATE User SET Name = :Name WHERE Id = :Id"];
Why is this as such? I would like to understand the logic/reasoning behind..
I am guessing that it has to do with the memory management of the string, it might not have been sufficiently retained so it is getting cleaned up before for it is getting used. The difference between the two methods are defined here
I have just found something interesting from this thread: How to refresh TableView Cell data during an NSTimer loop
This, I believe, is the reasoning behind..
I quote what "petergb" said:
[NSString stringWithFormat:...] returns an autoreleased object. Autoreleased objects get released after control returns from the program's code to the apple-supplied run-loop code. They are more or less a convenience so we don't have to release all the little objects that we use once or twice here and there. (For example, imagine how tedious it would be if you had to release every string you created with the #"" syntax...)
We can tell stringWithFormat: returns an autoreleased object because, by convention, methods who's names don't start with alloc or copy always return auto-released objects. Methods like this are said to "vend" an object. We can use these objects in the immediate future, but we don't "own" it (i.e. we can't count on it being there after we return control to the system.) If we want to take ownership of a vended object, we have to call [object retain] on it, and then it will be there until we explicitly call [object release] or [object autorelease], and if we don't call release or autorelease on it before we lose our reference to it by changing the variable to something else, we will leak it.
Contrast with [[NSString alloc] initWithFormat:. This method "creates" an object. We own it. Again, it will be there until we explicitly call [object release].

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]

Correct use of autorelease

I am trying to understand why my application crashes and I am going through my code. I am pretty sure that this is a valid use of autorelease:
(part of the code)
- (NSArray *)allQuestionsFromCategories:(NSArray *)categories {
...
NSMutableArray *ids = [[[NSMutableArray alloc] init] autorelease];
while (sqlite3_step(statement) == SQLITE_ROW) {
[ids addObject:[NSNumber numberWithInt:sqlite3_column_int(statement, 0)]];
}
return [NSArray arrayWithArray:ids];
}
Is this valid? The NSArray arrayWithArray returns an autorelease object doesn't it? I also have some difficulties in understanding the scope of autoreleased objects. Would the autoreleased object (if it is in this case) be retained by the pool through out the method that invoked the method that this code is a part of?
- (void)codeThatInvokesTheCodeAbove {
NSArray *array = [self.dao allQuestionsFromCategories];
...
}
Would the array returned be valid in the whole codeThatInvokesTheCodeAbove method without retaining it? And if it was, would it be valid even longer?
Got some issues understanding the scope of it, and when I should retain an autorelease object.
That is valid, but -- really -- you can just skip the [NSArray arrayWithArray:ids]; entirely as there is no need to create a new array.
An autoreleased object is valid until the autorelease pool is drained, which typically happens once per pass through a run loop (or "periodically, but never while your block is executing" when enqueuing stuff via GCD).
The documentation explains it all better than I.
There is no need to create an immutable array because the return will effectively "up cast" the NSMutableArray to NSArray. While this is meaningless at run time, it means that the caller cannot compile a call to a mutating method of the returned value without using a cast to avoid the warning. Casting to avoid warnings in this context is the epitome of evil and no competent developer would do that.
If it were an instance variable being returned then, yes, creating an immutable copy is critical to avoid subsequent mutations "escaping" unexpectedly.
Have you read the memory management documentation? Specifically, the part about autorelease pools? It makes it quite clear how autorelease works. I hate to paraphrase a definitive work.
[NSArray arrayWithArray:] returns an autoreleased object. If you want codeThatInvokesTheCodeAbove to take ownership of the array, you should call retain on it (and rename codeThatInvokesTheCodeAbove according to apple's guidelines). Otherwise, if you don't care that ownership of the object is ambiguous then your code is okay.
In other words, [NSArray arrayWithArray:] returns an array that you don't own, but you have access to it for at least this run cycle. Therefore, codeThatInvokesTheCodeAbove will have access to it for at least this run cycle. Ownership is not clear, since nobody called alloc, copy, new, or mutableCopy or retain. It is implied that NSArray called autorelease before returning the new array, thus relinquishing ownership.
My information comes from http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmRules.html%23//apple_ref/doc/uid/20000994-BAJHFBGH.
So, to answer your question, yes your posted code is valid. Whether it's correct depends on what it is you are trying to accomplish.
Autoreleased object are objects that are marked as to be release later.
There is a very particular object that is automatically created by UIApplicationMain: an UIRunLoop.
Imagine it like a while structure, it cycle until application is terminate, it receives every event and properly resend it to your interested classes, for example. Just before UIApplicationMain there is a command to create an NSAutoreleasePool that, once the NSRunLoop is ready, attach to it. When you send an -autorelease command to an object, the NSAutoreleasePool will remember to release it when is released itself. It's dangerous to use it many times in platforms that has less memory (iOS devices), because objects aren't released when you send an -autorelease command but when the NSAutoreleasePool is drained (when app releases it).
If you want to free the non-mutable list before you return, you can use something like this:
- (NSArray *)allQuestionsFromCategories:(NSArray *)categories {
...
NSArray* result;
NSMutableArray *ids = [[NSMutableArray alloc] init]; // AUTORELEASE REMOVED FROM HERE
while (sqlite3_step(statement) == SQLITE_ROW) {
[ids addObject:[NSNumber numberWithInt:sqlite3_column_int(statement, 0)]];
}
result = [NSArray arrayWithArray:ids]; // COPY LIST BEFORE IT IS FREED.
[ids release]; // MUTABLE LIST FREES _NOW_
return result; // NONMUTABLE COPY IS RETURNED
}
It is not worth doing this unless your mutable array is likely to sometimes use a lot of memory.

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.

NSDictionary leak on iPhone and object reference graph

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.