OK, so I am halfway through developing an iPhone app and I keep stumbling when it comes to memory management.
I have tried a number of times to understand this with very limited success. I consider myself to be above average intelligence, but this stuff just eludes me, despite repeated searches and reading of Apple documentation
Lets say I have a picker that I am creating - so the code goes
UIPickerView *patientPicker = [[[UIPickerView alloc] init]retain];
//more code here
[self.view addSubView:patientPicker];
So then I do a couple of different things with my picker.
The picker only appears when a segmented control button is pressed. The segmented control dictates which array of data is used to populate the picker.
However, when I change segmented control, I find that it displays a new picker on top of the old picker, rather than changes the data in the current picker.
i.e. segmented control is patient age or weight. If age is selected a picker of ages appears, and the same if weight is selected the picker of weight appears. However if one of the pickers is already present, then clicking on the alternate segment doesn't change the data, it just adds another picker onto the view.
My problem comes when I try and hide the picker as the old picker is still underneath, and I can't hide the old one.
So when I click a button to remove the picker, the old picker is still present underneath.
I have tried
[patientPicker removeFromSuperView];
but when I try and rebuild my picker I am advised that patient Picker has been deallocated???
The same goes for
[patientPicker release];
I know that someone would be able to tell me the simple answer, but what I really want is a really simple/dumbed down explanation of memory management so that I don't have to ask again.
Pretend I am 7 years old!
Thanks
Bob
UIPickerView *patientPicker = [[[UIPickerView alloc] init] retain];
Here you don't/shouldn't do a retain. The init call already implies that the caller is responsible for the create object (in other words, init has a retain implied). You need a release for every init and retain.
I reckon that you doing an alloc/init each time the segmented control changes selection. What you can do is, In the vieDidLoad, do this:
UIPickerView *patientPicker = [[UIPickerView alloc] init;
//more code here
[self.view addSubView:patientPicker];
patientPicker.hidden = YES;
[patientPicker release];
When the selection is made on segment control, set the hidden property of the picker to NO and set the datasource according to the selection
this sounds like a job for a tool... yes! hit Build and Analyze and remove every issue =) but understand why the static analyzer flags your program, and understand that there's quite a bit it can catch, yet quite a bit that it can't prove is a reference count imbalance and will not flag these. then run with leaks instrument and fix all issues, etc. and if you run into deallocated instances, run zombies instrument and fix all issues.
anyways, there's more to it! here are some points to your code.
UIPickerView *patientPicker
= [[[UIPickerView alloc] init]retain]; // << do not retain here. alloc
// returns an object you must release
[self.view addSubView:patientPicker]; // << self.view will retain its subviews.
// ok, that makes sense that the view
// would want to hold onto a reference to
// ensure the view is not destroyed
// while it's still a subview.
[patientPicker removeFromSuperView]; << the superview will release its subview
[patientPicker release]; << your life will be easier if you use the accessors
when you're dealing with reference counting, you need to hold a reference to use an object. so, let's take autorelease pools out of the equation. using autorelease only when needed will help you learn, and make some of your issues local to the callsite -- avoid calling autorelease where possible while you are learning.
NSString * a = [[NSMutableString alloc] init]; // << I hold 1 reference
[a length]; // ok
[a retain]; // << I hold 2 references
[a release]; // << I hold 1 reference
[a release]; // << I hold 0 references
[a length]; // expect bad things
now let's illustrate autorelease pools:
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSString * a = [[NSMutableString alloc] init]; // << I hold 1 reference
[a length]; // ok
[a retain]; // << I hold 2 references
[a release]; // << I hold 1 reference
[a autorelease]; // << add a to pool. when pool is destroyed a release message will be sent to a
[a length]; // ok. a is still alive. its release message is deferred until the pool is destroyed
[pool release]; // pool's reference count has reached zero. pool will call [a release]. pool will be destroyed.
[a length]; // expect bad things
Two rules of memory management:
If you new, alloc init, retain, or copy (N.A.R.C.) an object, you have to release it.
When the name of a method starts with any of those words, it means it's being created for the caller, who has the responsibility to release the object when he is done with it. Otherwise the method returned is not owned by the caller, and he has to indicate he wants to keep it calling retain on the object.
Note that the first rule results in the second. A method creates an object (therefore it is responsible for releasing it) but if the outcome of the method (the returned object) survives the execution of the method (so it can be handed to the caller) all the method can do is autorelease the object. An autorelease adds the object to the autorelease pool, which will release the object at some point in the future.
Example:
[[MyObject new]; // 1)
[NSString initWithFormat:#"%d",1]; // 2)
[NSString string]; // 3)
1) Name contains new, so it will need release.
2) Name contains init, so it will need release.
3) Name doesn't contain any of the NARC words so you know it returns an autoreleased object. This means you need to retain it if you intend to keep it, and if you do, you will need to release it later. Otherwise just use it and forget about it.
Couple of tips:
Try to retain/release symmetrically so you don't lose track of what to release and where. Example: if you retained on init, then release on dealloc. Same for viewDidLoad/viewDidUnload.
If you can choose, don't abuse autorelease when memory is a concern so you recover your memory as soon as possible. Abusing autorelease is also a sign you don't understand memory management.
Your example (same thing Justin told you):
UIPickerView *patientPicker = [[UIPickerView alloc] init]; // 1)
[self.view addSubView:patientPicker]; // 2)
[patientPicker release]; // 3)
1) We call alloc init so you know this object will need release once you are done with it.
2) addSubView calls retain internally. Why? When you receive an object and you intend to keep it, you express that intention calling retain, which also gives you the responsibility of releasing it. When a UIView is released, its implementation also releases its subviews to balance the retain done before by addSubView.
3) We won't be using it anymore so we call release. Now self.view is the owner of the object.
As an exercise, try to implement your own setter and getter for a variable and run Build and Analyze to see what Xcode thinks about it.
Focus on Ownership, if you have ownership; you have to release object. If you don't have ownership don't dare to release it.
id myObject;
myObject = [abc retain] => you are the owner
myObject = [[abc alloc] init] => you are the owner
myObject = [abc copy] => you are the owner
myObject = [[[abc alloc] init] autorelease] => you are not the owner
(everywhere you put autorelease you loose the ownership)
myObject = [abc xxxWithYYY] => you are not owner
(as a convention, a method returning object always give an autorelease object)
There will be some more similar conventions where you can identify OWNERSHIP; I just jotted down what I can recall now.
Related
I noticed that my program was crashing because it was running out of memory. I figured out that this was happening because of this code segment:
DataSet *tempSet = [[DataSet alloc] init];
tempSet.rightFoot = [NSNumber numberWithDouble:temp1];
tempSet.leftFoot = [NSNumber numberWithDouble:temp2];
[footData addObject:tempSet]; //add dataSet object to the array
[tempSet release];
I read some tutorials about memory management online and was able to figure out that I needed to do this: (notice the added "autoreleases")
DataSet *tempSet = [[DataSet alloc] init];
tempSet.rightFoot = [[NSNumber numberWithDouble:temp1] autorelease];
tempSet.leftFoot = [[NSNumber numberWithDouble:temp2] autorelease];
[footData addObject:tempSet]; //add dataSet object to the array
[tempSet release];
I am still confused about why I had to do this. I did not use alloc, new or copy when creating the numberWithDouble.
Does this mean that I would need to add autorelease in this situation as well?:
[subset addObject:[NSNumber numberWithDouble:temp]];
What about this situation?:
tempSet.rightFoot = [NSString stringWithString:#"temp"];
I appreciate any help.
+numberWithDouble
is called a convenience method. Meaning, it replaces the little section of code that would look like this:
[[[NSNumber alloc]initWithDouble:double]autorelease];
Most (if not all) convenience methods are auto release by default, so the OP code with the autoreleases is incorrect, as it drops the retain count to -1.
The equals sign however is equivalent to
[self setRightFoot:[[[NSString alloc]initWithString]autorelease]];
which increments rightFoot's retain count and requires it to be released elsewhere.
as for the -addObject code, it returns void, so it does not in fact increment the receiver's retain count, and requires no release of the receiver. The object in the array should already be released by the convenience method for later, which doesn't matter because the array is now holding "copy" of it.
This is not an answer (I just do not know how to comment -- I only see "share, edit, flag"), but just a few info about Memory Management in iOS:
1. Don't release objects that you do not own.. ---> owned objects are usually the ones you "alloc", "new", "copy." //And probably the one in your #property wherein you "retain" an object.
2. when you "autorelease" an object, don't "release" it afterwards, because that would mean you're releasing the same object twice.
But there's ARC already, so you better upgrade your Xcode to avoid overreleasing objects / memory leaks (for not releasing objects)..
If there's something wrong or inappropriate with the one I put here, please edit. :)
When running Analyzer, I receive the "Object sent -autorelease too many times" warning. I know I'm doing something tricky (and am open to alternatives to accomplishing my goal).
Essentially, I wanted to keep a stack of specific types of controllers handy for sending messages from a central location. So, considering:
From 'ActionBroker' shared object:
NSMutableSet *liveActions;
liveActions = [NSMutableSet alloc] init];
...
CLViewController *action = [[[actionClass alloc] init] autorelease];
if (!action) {
return nil;
}
[self.liveActions addObject: action];
// When adding an object, the retain count is increased. We want the action
// to dealloc, so that it might remove itself from the list...
[action release];
return action;
And the complementary dealloc code:
[[CLActionBroker sharedActionBroker] removeAction: self];
[super dealloc];
... and in removeAction:
[action retain];
[self.liveActions removeObject:action];
The above code works, I just get the nagging error. And the nagging sensation that I could probably solve the problem a different way.
One of the use cases for this code is to pass an 'handleOpenURL' request through a chain of open controllers and returning the first 'YES' response.
As I understand it, your intent is to have a set of controllers ("actions") in the set, which you could easily access from anywhere in your app. If one of these controllers is deallocated, it would automatically remove itself from the set.
There's a problem with this setup, though. As you've described it above, the controller is supposed to be removed from the liveActions set upon deallocation. But since an NSSet retains its members, your controller will never be deallocated so long as it is still in the liveActions set. -dealloc is only run when all retains have been balanced by a release.
This, then leads to your over-release, which leads to the warning. Since you've sent an extra release, -dealloc could be run while the controller is still in the liveActions set. But when you remove it from that set, it's going to send a release message to your object, which would take its retain count negative. That may or may not be safe, but in either case it's an ugly workaround.
What you really want, it seems, is a set that does not retain its members. This is normally a dangerous configuration, since it can lead to dangling pointers in the set. But if you're willing to manage the lifetime of the object and clear out those dangling pointers at the appropriate times, it turns out that it's quite doable. You just need to use a CFMutableSet instead of an NSMutableSet. And since the two are toll-free bridged, it doesn't even add that much complexity.
Setting up the CFMutableSet looks like this:
// The NULL for the third parameter tells the set to not do anything when
// an object is added to or removed from the set.
CFMutableSetRef cfLiveActions = CFSetCreateMutable(NULL, 0, NULL);
// Toll-free bridging makes this possible
NSMutableSet *liveActions = (NSMutableSet *)cfLiveActions;
After that, you can use it exactly as you would use any other NSMutableSet; this one will just be special in that it won't retain its members.
The problem:
CLViewController *action = [[[actionClass alloc] init] autorelease]; // +0 ownership
// ...
[self.liveActions addObject: action]; // liveActions takes ownership
// ...
[action release]; // -1 ownership
When you've alloced the object, you are responsible for it. But then when you autorelease it, you've fulfilled your obligation (and because you autoreleased instead of releasing, you can still use the object until the next turn of the runloop). You shouldn't release it again later.
Also:
[action retain];
[self.liveActions removeObject:action];
The retain is unnecessary.
(And here's another plug for beginning to switch to ARC, under which you won't have to worry about this!)
[Edit: misunderstood your question. Revised answer forthcoming.]
[Edit 2: never mind... even though I misread your intent, I believe my solution is still right.]
I am subclassing NSURLConnection, and used MGTwitterEngine as a base to help me get started. That may be irrelevant. However, I noticed in their code they don't use #property or #synthesize for their ivars. They have wrapped the ivars in accessor methods which look like this:
- (NSString *)identifier {
return [[_identifier retain] autorelease];
}
My question is two part. First, what effect does retain followed by autorelease have? It seems to me it would cancel itself, or worse yet leak.
Second, if I were to change the header file to have:
#property (nonatomic, retain, readonly) NSString* _identifier;
And used #synthesize indentifier = _identifier, wouldn't this do the same thing as the accessor method without having to write it?
Maybe it is just two different ways to do the same thing. But I wanted to ensure I have the correct understanding. Thanks.
Using #synthesize will actually only create a setter and a getter method. The code that is auto generated for you is guaranteed to use proper memory management, so that you do not need to worry.
MGTwitterEngines use of return [[ivar retain] autorelease] is actually the correct way to do it. Lets have two examples.
Assume a getter is defined as this:
-(Foo)foo {
return foo;
}
And then we execute this code:
bar = [[bar alloc] init]; // bar has aretain count of 1.
foo = bar.foo; // foo har a retain count of 1 (owned by bar).
[bar release]; // Bar and all it's ivars are released imidiatetly!
[foo doSomething]; // This will crash since the previous line released foo.
If we instead change the getter to this:
-(Foo)foo {
return [[foo retain] autorelease];
}
bar = [[bar alloc] init]; // bar has a retain count of 1
foo = bar.foo; // foo has a retain count of 2 (one owned by bar, 1 owned by autorelease pool).
[bar release]; // Bar and all it's ivars are released imidiatetly!
[foo doSomething]; // Will not crash since foo is still alive and owned by autorelease pool.
Hope this explains why you should always return properly autoreleased objects from all your getters. It is important that any return value can survive the deallocation of it's parent, since no class ca guarantee what a client will do with it's values once it is exposed to the wild.
Retain followed by autorelease does exactly what you might think it does. It sends the object a retain message and then sends it an autorelease message. Remember that autoreleasing an object adds that object to the autorelease pool but does not release it yet. The autorelease pool will send the object a release message at the end of the current iteration of the run loop. So, a retain followed by autorelease essentially says, "Make sure this object stays around until the end of the the current iteration of the run loop." If you need the returned value to hang around longer, you can retain it. If not, do nothing and the autorelease pool will handle it.
In this case, the string being sent retain and autorelease messages is a property. It's already retained by the parent object. So you might wonder why do this retain and autorelease thing at all? Well, there's no guarantee that the object won't release _identifier before the current iteration of the run loop ends. Consider this example:
- (NSString *)identifier { return _identifier; }
- (void)aMethod {
NSString *localId = [self identifier]; // localId refers to _identifier which is only retained by self
[self methodThatChangesIdentifierAndReleasesItsOldValue]; // self releases _identifier
NSLog(#"%#", localId); // crash, because localId (old value of _identifier) has been released
}
In this case, the returned identifier isn't retained and autoreleased. The NSLog line crashes because localId refers to a released string. However, had we used retain and autorelease in the getter, there would be no crash because localId would not be released until the end of the current iteration of the run loop.
As far as I know, your replacement with an identifier property would be just as good. It might not be identical code, but the effect should be the same.
Apple explains this well in Implementing Accessor Methods. The method quoted by you (named "Technique 1" by Apple) helps avoid bugs if the caller assigns a new value to the identifier property and then expects the retrieved value to still be available. It won't be needed most of the time but just returning the ivar value can lead to bugs that are hard to track down.
Generally one retains then autoreleases if you are returning something that you didn't own the first place. It will only leak if you own _identifier and there is a mismatch of retain/alloc/etc versus release/autorelease sent to that object.
Secondly, yeah you generally don't have to write the headers if you aren't doing something special beyond what the general boilerplate looks like. Apple has good documentation on properties, I suggest if you're still fuzzy, you look at those.
I've subclassed some UITextField and added some custom properties.
In a UITableViewController, in the ViewDiDLoad I init them, and in the cellForRowAtIndexPath I add them to the cell with [cell.contentView addSubview:customTextField];
Each cell has a different customTextField as all of them are very different.
Where I should call the [customTextField release] ?
After I add them to the cell view ?
If for example I call [self.tableView reloadData] my customTextField are going to be added again to the cell, so maybe I should change my approach in doing this ?
thanks for the orientation ...
regards,
r.
You release an object when you no longer have any interest in it. This happens for many reasons; it might be because you've finished with the object, or have passed the control over the object lifetime to another object. It might be because you're about to replace the object with a fresh instance, it might be because you (the owner object) are about to die.
The last one seems to be relevant in your case. You create these object in viewDidLoad and repeatedly need them (i.e. to add them to cells) until your object is no longer functioning. In this case, just as you create them in viewDidLoad, you can release them in viewDidUnload.
Edit: I should really mention autorelease, but this isn't relevant in this instance. Memory management is best handled with a notion of 'owner' - the person who creates something (or retains it) should be responsible for deleting it (or releaseing in ObjC parlance). autorelease handle some cases where you need to give an object to an alternate owner, having previously owned it yourself (typically via a method return). If you are the creator, you can't just release it before returning it to the new owner, as it will be deleted before the new owner has a chance to stake an interest in it. However, you can't just not release it; it will leak. As such, the system provides a big list of objects that it will release on your behalf at some point in the future. You pass your release responsibility to this list, then return the object to the new owner. This list (the autorelease pool) ensures your release will occur at some point, but gives the new owner a chance to claim the object as theirs before it's released.
In your case, you have a clear interest in owning the objects for the lifetime of your view controller - you need to, at any time, be able to add them to view cells in response to a table data reload. You're only done with them when your view controller dies, so the viewDidUnload (or possibly dealloc) is the only sensible place to release them.
I always release my controls directly after I added them to a view using addSubView. When I work with tables, I also initialize them in the cellForRowAtIndexPath method.
Therefor the object stays alive the shortest time.
Adam Wright explains the theory of this very well, but let me give you some practice. You're thinking about this problem far too hard, and that almost always leads to bugs. There is a simple solution that solves this problem almost every time: Retain all ivars using accessors; don't retain non-ivars.
.h
#interface ... {
UITextField *_customTextField;
}
.m
#property (nonatomic, readwrite, retain) UITextField *customTextField;
...
#synthesize customTextField=_customTextField;
-(void)viewDiDLoad {
self.customTextField = [[[UITextField alloc] init....] autorelease];
}
...
- (void)dealloc {
// I do not recommend accessors in dealloc; but everywhere else I do
[_customTextField release]; _customTextField = nil;
}
Never access your ivars directly, except in dealloc (even that is controversial and some people recommend self.customTextField = nil; in dealloc; there are arguments either way). But never assign your ivars directly. If you will follow this rule, you will find that most of your memory problems go away.
The safest way to handle object ownership is to autorelease the view directly after initialization:
FooTextField* textField = [[[FooTextField alloc] init] autorelease];
[myCell.contentView addSubview:textField];
Adding the text field to a superview (the UITableViewCell's contentView) retains it. This way you don't have to care about releasing the view afterwards.
There seems to be a resentment against autorelease in the iPhone developer community. In my opinion, this resentment is unfounded. Autoreleasing an object adds very little overhead to the program if the objects lives longer than the current pass through the run loop.
Every object in Obj-C has a reference counter (retainCount), and when this counter goes to 0 the object is deallocated. When you allocate and initialize an object, the reference count is set to 1 - but you can retain it as many times you want to.
UITextField *textField = [[UITextField alloc] init]; // Reference counter = 1
[textField retain]; // Reference counter = 2
[textField retain]; // Reference counter = 3
The opposite of retain is release, which subtracts from the reference counter;
...
[textField release]; // Reference counter = 2
[textField release]; // Reference counter = 1
You can always get the reference counter of your objects;
printf("Retain count: %i", [textField retainCount]);
The method addSubview of UIView does retain your passed in sub view - and when it's done with it it releases it. If you need your UITextField later, in another scope (when the UIView is done with it and has released it) - you should not release it after you've added it to the super view. Most of the time you actually don't need to hold on to a reference, so you should release it after you've added it to the super view. If you dont - you can release it in the dealloc method of your scope.
Take a look at UITableView -dequeueReusableCellWithIdentifier: and -initWithStyle:reuseIdentifier:.
In -tableView:cellForRowAtIndexPath:, use -dequeueReusableCellWithIdentifier: and check if the result is nil. If it is, instantiate a new cell with -initWithStyle:reuseIdentifier:.
Send -autorelease to your customTextField upon creation and adding to the respective cell.
You should not add subview in cellForRowAtIndexPath! This will slow down the view as you add a subview each time the cell is displayed. Try using custom UITableViewCell class for that purpose.
Here is a perfect solution of UITableView customization
http://cocoawithlove.com/2009/04/easy-custom-uitableview-drawing.html
works jut perfectly
So I modified Apple's PageControl example to dynamically load various navigation controllers (along with their root view controllers) into the scroll view. I also added a technique that attempts to unload a navigation controller when it's no longer needed. I've only been at ObjC for a little over a month, so I'm not sure if I'm doing the unloading correctly. Please see my code below, followed by my questions.
First I create a mutable array and fill it with nulls, just like Apple does:
// Create dummy array for viewControllers array, fill it with nulls, and assign to viewControllers
NSMutableArray *array = [[NSMutableArray alloc] init];
for (unsigned i = 0; i <= kNumberOfPages; i++)
{
[array addObject:[NSNull null]];
}
self.viewControllers = array;
[array release];
...Later, I fill the array with UINavigationController objects like so (this is just partial code, please excuse the missing parts...the main idea is that I alloc a couple of things, assign them and then release):
id controller = [[classForViewController alloc] initWithNibName:NSStringFromClass(classForViewController) bundle:nil];
navController = [[UINavigationController alloc] initWithRootViewController:controller];
[controller release];
[self.viewControllers replaceObjectAtIndex:page withObject:navController];
[navController release];
...Finally, if a page doesn't need to be loaded anymore I do this:
[self.viewControllers replaceObjectAtIndex:i withObject:[NSNull null]];
Questions:
My understanding is that once I replace the navigation controller in my viewControllers array with null, the array releases the navigation controller. Thus the navigation controller's retain count hits zero and it no longer takes up memory. Is this correct?
What about the root view controller inside the navigation controller? Do I need to do anything with it or does it get released automatically once the navigation controller's retain count hit zero?
Thanks!
Yes. Any object put into a collection is sent a retain message. Likewise any object removed from a collection is sent a release message, the cause of the removal is irrelevant.
Yes, all objects will release all the objects it owns when they are released.
This all boils down to the simple principle of ownership that Cocoa defines:
You own the object if you received it as return value by calling a method that:
Is named alloc or new.
Contains the word copy, such as copy and mutableCopy.
You own the object if you call retain.
You may only call release and autorelease on objects you own.
You must release all owned objects in your dealloc methods.
There is just one exception; delegates are never owned. This is to avoid circular references and the memory leaks they cause.
As a side effect this also means that when you yourself are implementing a method, you must return an auto released object unless you are implementing new, or a method with copy in it's name. Objects returned as out arguments are always autoreleased.
Follow this strictly and Objective-C can be treated as if it is garbage collected 95% of the time.
Presumably yes, once your retain count reaches zero (however or whenever that happens) your object will receive the dealloc message. You can put a breakpoint to ensure that is happening. Instruments comes with a Leaks utility that should help you find memory problems, it's a great tool and I suggest using it frequently.
I'm not quite sure what you mean by "do anything to it". I presume you mean release it. The general pattern is that if you alloc or retain, you release. You can roughly guess if there is going to be a problem if your allocs and retains outnumber your releases (or vice versa, you don't want to double release).