I'm using the iPhone SDK 3.0, but I think this is a general misunderstanding of how things work w/ c & memory management.
I've overridden the viewWillAppear method like this
#implementation MyViewController
- (void)viewWillAppear:(BOOL)animated {
NSArray *items = [NSArray arrayWithOjbects:self.searchButton, self.trashCan, nil];
[self.bottomBar setItems:items animated:YES];
}
// other stuff...
#end
when I try to switch away from the view controller above and switch back everything works properly.
BUT, my inclination is to "release" the original pointer to "items" because I think a reference to the NSArray is now held by bottomBar.
But when I do this (see code below) and try to switch away from the UIViewController, I get a memory management error (-[CFArray count]: message sent to deallocated instance 0xd5f530).
- (void)viewWillAppear:(BOOL)animated {
NSArray *items = [NSArray arrayWithOjbects:self.searchButton, self.trashCan, nil];
[self.bottomBar setItems:items animated:YES];
[items release];
}
Do I need to not release items in this case? Or am I doing something wrong?
Obviously, the empirical evidence indicates that I shouldn't release "items", but it's not clear to me why this is the case.
Thanks for any info/"pointers"!
You do not need to release it because you never init'd it. [NSArray arrayWithObjects:...] returns an autoreleased object. You are not responsible to release it, because it has had the autorelease message sent to it when it returned from the method. You only have to release what you init! (If you had used [[NSArray alloc] initWithObjects:...] you would have had to.)
When you call arrayWithObjects: on NSArray:
NSArray *items = [NSArray arrayWithObjects:self.searchButton, self.trashCan, nil];
You are returned an autoreleased array. The array is returned to you autoreleased, because you do not call alloc, new, or a method containing copy on it. This signifies that you do not need to memory manage that object. (Take a look at the Memory Management Programming Guide for Cocoa for more information)
However, it is then retained when you call setItems on self.bottomBar, passing the array as an argument, bumping its retain count up to 1, but then you release it, returning its retain count back to zero, which causes it to be deallocated.
Since the array is retained by self.bottomBar, this implies that it is managing the memory of the array. When it is no longer needed, the array will be released, implying that the class no longer needs the array, which is the correct way to manage the memory.
For heavens sake guys, just point people to the Memory Management Rules. Don't paraphrase them. Don't say "returns an autoreleased object" (which is not necessarily true, and is irrelevent even when it is true). Just point them to the rules.
The rules are a sum total of 9 paragraphs! There is no need to paraphrase them, abrieviate them, or restate them. They are clear and concise and explicit.
Read the rules, follow the rules, and you will have no memory management problems.
Here's the short version:
+[NSArray arrayWithObjects:] returns an object that you do not own, so no, you should not release it.
On the other hand, if you had done:
NSArray *items = [[NSArray alloc] initWithObjects:self.searchButton, self.trashCan, nil];
this creates an object with a retain count of 1, so you would need to release it to prevent it from leaking.
Check out the Memory Management Programming Guide for Cocoa for more details.
Related
could the following code lead to problems?
- (void) method1 {
NSMutableArray *myArray = [[[NSMutableArray alloc] init] autorelease];
... fill the array
[someObject someMethod:myArray]; // someObject does work on that array (without retain!)
}
A sometimes-appearing crash in my app looks like it IS a problem; but I would not understand that... shouldn't myArray stay alive at least until the end of method1?
Thanks a lot for your help!
So my questions besides "can that be a problem" are:
- would it be enough to remove autorelease and make a release at the end of the method?
- if not: do I have to make a retain/release in "someMethod"?
EDIT: but this can be a problem, am I right?
- (void) method1 {
NSMutableArray *myArray = [self getArray];
... fill the array
[someObject someMethod:myArray]; // someObject does work on that array (without retain!)
}
- (NSMutableArray) method2 {
return [[[NSMutableArray alloc] init] autorelease];
}
I'm going to assume for a moment that you meant this to be NSMutableArray rather than NSArray. If you really mean NSArray here, then it is all impossible. You can't "fill the array" on an immutable array.
Yes, this should work. Your problem is likely elsewhere. A common mistake here would be over-releasing something you put into myArray.
Looks like you're missing a couple of open brackets on the line where you allocate your array. Should be [[[NSMutableArray alloc] init] autorelease];
Your code is completely correct. Do not listen to people telling you to remove the autorelease and manually release the array after the call to someMethod:.
In 99% of cases using an autorelease'd object has absolutely no negative performance impact on your application. The only time you want to worry about it is in loops.
The [[[Foo alloc] init] autorelease] pattern is just the same as using a built-in helper method like [NSString stringWithFormat:...]. They both return an autoreleased object, but you probably don't worry about performance with the latter. Again, only worry about autorelease in large loops, or when Instruments tells you you have a problem.
Using the [[[Foo alloc] init] autorelease] style also keeps all the memory management on a single line. If you get used to typing [[[ you won't forget the release. If you are copying and pasting code, or moving code around, you won't accidentally lose the corresponding release because it's all on the same line.
It's also easier to review code that uses the [[[Foo alloc] init] autorelease] style because you don't have to go around hunting for the release further down the method to make sure the memory management is correct.
So in terms of readability and safety and correctness, your code is absolutely fine and good. This applies to both your original snippet and the follow up you added below. Performance of autorelease only becomes an issue when you have large loops.
Your crashing issue must be caused by some other factor that is not evident in the code you posted.
I also suggest you read the Memory Management Programming Guideline if you have not already. Basically, and autorelease'd object is guaranteed to remain valid until the enclosing pool is released. In your case, the autorelease pool exists higher up in the call stack (probably the main runloop) so you can be safe in the knowledge that your autorelease'd array will remain valid for the duration of any calls you make.
One last point. Your array allocation code could also make use of the array helper constructor:
NSMutableArray *myArray = [NSMutableArray array];
This is even simpler, cleaner and shorter than your original.
Best thing to do will be something like that:
- (void) method1 {
NSMutableArray *myArray = [[NSMutableArray alloc] init];
... fill the array
[someObject someMethod:myArray]; // someObject does work on that array (without retain!)
[myArray release];
}
and it's best from two different angles. One way you are holding your array till you have no need for it, so it will no wipe from memory until you say so. Second is the same sentence, only the reason is that you will clean up memory from no more needed objects as soon as it could be done.The last may need some more explanation... Autoreleased objects should be autoreleased as soon as you have no need for it, but they are released on two basic rules: at the end of block(not always) or at the memory pressure after end of the block(yes always). In other words NSAutoreleasePool doesn't always release stuff at the end of some block of the code, and it's not even close to be rare that it delays those releases to later point.Anyway you should always check for over-releasing your object as it will lead to crash when you'll try to reach such object at your code.
XCode is reporting a memory leak on a specific line of code:
(NSArray*)myFunction{
NSMutableArray * tempMapListings=[[NSMutableArray alloc] init]; //Xcode says leak is here
//do a bunch of stuff to insert objects into this mutable array
return tempMapListings;
[tempMapListings release]; // but I release it ?!
}
Is this due to releasing as an NSArray an mutable array? Since mutable inherits from inmutable, I wouldn't think this is a problem, and in any case, the object is released anyway. I'd appreciate the advice of a second eye.
No you're not releasing it. The return statement really ends the execution of the method at that point. So, the line below it, in your case
[tempMapListings release]; // but I release it ?!
is not executed.
Instead, you use autorelease:
-(NSArray*)myFunction{
NSMutableArray * tempMapListings=[[NSMutableArray alloc] init];
//do a bunch of stuff to insert objects into this mutable array
return [tempMapListings autorelease];
}
You can learn about autorelease in many places. Look for it in Apple's own documentation; you can also google it.
You're releasing tempMapListings after your return from the function. After a return statement, no more code is executed on that branch. Ergo, your [tempListListings release] statement is never run. Moreover, as you're returning it, you don't actually want to release it straight away - the caller will never have a chance to retain the array!
Autorelease pools are your friend here. Objects added to an autorelease pool are released on your behalf "eventually", giving your caller time to grab the result. To add your object to the default pool, change your allocation line to
NSMutableArray *tempMapListings = [[[NSMutableArray alloc] init] autorelease];
and remove that last release call.
For more information on autorelease pools, have a read of Apple's documentation. They're really quite useful.
I am trying to get the hang of retain / release. I get that they are a matched set. But I don't know when I have to retain references.
-(void)sampleMethod:(RandomClass *) obj {
[obj retain];
// Do stuff to object...
[obj release];
}
Is it necessary to retain (and thus release) obj?
I am worried about obj going away. Does it follow that you must (if) retain reference parameters as soon as possible in the function? What about the space of time between the functions call and the first instruction of the function?
Thanks!
Short answer; use ARC.
Joe's answer is more or less correct. Until it isn't.
In general, there is no need to retain arguments or return values from other methods. However, the resulting code only works by coincidence and convention, not by algorithmic analysis.
Consider:
NSString *foo = [aMutableArray objectAtIndex: 5];
[aMutableArray removeObjectAtindex: 5];
[someTextField setTextValue: foo];
BOOM!
Your code just crashed. Maybe (it won't crash if foo happens to be a constant string or happens to have been retained by something else or happens to have been retain/autoreleased somewhere else).
Technically, that should be:
NSString *foo = [aMutableArray objectAtIndex: 5];
[foo retain];
[aMutableArray removeObjectAtindex: 5];
[someTextField setTextValue: foo];
[foo release];
That is, foo should be retained the moment it comes into scope and released the moment it is no longer used in a scope. Or you could [[foo retain] autorelease];, but autorelease pressure can be a problem (it generally isn't, but it can be).
ARC does this kind of analysis and would ensure that foo is retained as shown above when necessary.
You do not have to worry about the object being passed going away so there is no need to retain it. Proper memory management* ensures that the object will live for the duration of your method because it will be within the same thread as the caller, therefore the autorelease pool for that thread should not be drained and the caller can not release the object until your method has returned. This even holds true with methods such as performSelectorInBackground because that will retain the argument.
*Proper memory management - This means each thread that uses auto released objects gets it own autorelease pool that is drained within the same context it is created and objects passed across threads are properly retained.
In my code, I define a NSDictionary in viewDidAppear like this:
dataDictionary = [[NSMutableDictionary alloc] init];
then later in a loadData method, I load a mutable copy of the NSDictionary like this:
[dataDictionary setObject:[receivedData mutableCopy] forKey:[theConnection description]];
Later, when I switch to a different view, I unload my dataDictionary to save memory. In viewDidDissappear, I put:
[dataDictionary release];
dataDictionary=nil;
and I also release dataDictionary in dealloc.
However, it seems that there is a memory leak related to mutableCopy, and this is the only mutableCopy that I make, so it must be from the mutableCopy shown above. Does anybody have any idea why this might be leaking? I am thinking that mutableCopy makes another allocation besides the allocation made for the NSMutableDictionary, but I'm not sure how to deal with that since the mutableCopy is inside the dictionary and the dictionary is released.
Thanks in advance...
the mutableCopy method is not returning an autoreleased object, so you're receiving your NSMutableData with a retain count of 1, then you add it to the dictionary which also retains it - which means it will not be destroyed when you remove it from the dictionary or when the dictionary is dealloced, you will lose any reference to it, and the object will be leaked.
so like someone else suggested, autorelease the mutable copy when adding it to the dataDictionary.
[dataDictionary setObject:[[receivedData mutableCopy] autorelease] forKey:[theConnection description]];
or do something like
NSMutableData *mutableData = [receivedData mutableCopy];
[dataDictionary setObject:mutableData forKey:[theConnection description]];
[mutableData release];
Try something like this
[dataDictionary setObject:[[receivedData mutableCopy] autorelease] forKey:[theConnection description]];
Like Benj and Zaky already mentioned, you should call autorelease on your mutableCopy, but you also have to make sure not to release dataDictionary in both dealloc and viewDidDisappear:. Since dataDictionary has a retain count of 1 when you create it, and both viewDidDisappear: and dealloc probably get invoked when your view is destroyed, you'll wind up trying to release an object that's already been freed.
Make sure you only release dataDictionary in dealloc and you won't see the "double free" error message you mentioned. Invoking "release" on an ivar in viewDidDisappear: is a risky proposition anyway, since viewDidDisappear: gets invoked multiple times throughout the lifecycle of a view (e.g., if other view controllers get pushed onto your navigation stack). If you want to make sure to save memory, it's best to create stuff in viewDidLoad and to release stuff in viewDidUnload. viewDidUnload gets called in low-memory situations, so it's exactly what you want in this situation.
You may want to check out this post for a detailed description about Cocoa reference counting conventions: Object ownership in stringWithString and initWithString in NSString
I am currently working on an demo app so I was a little sloppy how to get things done, however I run the "Build and Analyze" to see how many leaks I get,... well and there are a lot.
Source of teh proble is that I have a NSMutableArray and I add some Objects to it :
NSMutableArray *arr = [[NSMutableArray alloc] init];
[arr addObject:[[MyObject alloc] initWithText:#"Option1"]];
// I have like 100 lines like that and 100 complains
Now, xcode complains about a potential leak.
Can someone give me some advice how to handle that ?
Thanks.
The problem is that you're allocating an instance of MyObject which you have a responsibility to release. When you pass it to the array, the array also retains the object, so now both you and the array have to release it. You can simply autorelease the object, and the array will keep it retained until you remove the object from the array or destroy the array itself.
[arr addObject:[[[MyObject alloc] initWithText:#"Option1"]] autorelease];
Replace
[arr addObject:[[MyObject alloc] initWithText:#"Option1"]];
with
[arr addObject:[[[MyObject alloc] initWithText:#"Option1"] autorelease]];
Most collections (arrays, dictionaries) own the objects added to them. And, since you’ve sent +alloc to MyObject, you also own the object that’s just been instantiated. As the memory management rules say, you are responsible for relinquishing ownership of objects you own. Sending -autorelease to the newly instantiated object will do that.