Memory management question - iphone

I wanted to clarify something:
I.e when I add a UIVIEW To i.e another UIVIEW programatically.
i.e [theview addsubview:thechildview];
I need to release thechildview because theview increments the retain count.
Right?
So basically if I have a loop and I am using that loop to initialise subviews and add the subviews to a view:
for(int i=0;i<10;i++){
UIView *child = [UIview alloc]init.....
[parent addSubview:child];
[child release];
}
I still need to release the child right ? So that would mean the child view still has a retain count of 1 right ?
The thing is whenever I call the release on the child after adding , the child gets deallocated after a while.
Can you guys confirm am doing the right thing by releasing the child after it is added ?
thanks.

You should release it somewhere, correct. You can hold on it and release it later, but it is your responsibility to do it somewhen.
The parent view will retain/release its childs on its own. So if the parent view gets dealloc'ed, it will release its child views.
If a child view is removed from its superview, it will also be released.

I need to release thechildview because theview increments the retain count.
Right
Wrong.
You need to release theChildView if you own it and if you have finished using it. Whether theview chooses to retain it or not is completely irrelevant.
So if we look at your loop
for(int i=0;i<10;i++){
UIView *child = [UIview alloc]init.....
[parent addSubview:child];
[child release];
}
the code is correct because at the beginning of the loop block you alloc the child which means you own it and at the end of the loop it is about to go out of scope which means you no longer need it so you must release (or autorelease) it.
What happens in between is totally irrelevant and you should not assume anything about what any particular method on another object does (except that it adheres to the Memory Management Rules).
I still need to release the child right ? So that would mean the child view still has a retain count of 1 right ?
You cannot assume anything about the retain count. parent might cause the object to be retained more than once if it hands it off to other objects. It might choose to copy it instead of retaining it. If it doesn't need to keep a reference it might not retain it at all.
Can you guys confirm am doing the right thing by releasing the child after it is added ?
No, you are doing the right thing by releasing it when you have finished using it.

Related

Releasing after pushing in iOS

So I have the following code where I am releasing an object after pushing it to another view. When I analyse it I get the error - Incorrect decrement of the reference count of an object that is not owned at this point by the caller. Would anyone know how to fix this? I've tried so many options each time getting a different memory leak
- (void)showCurrentArticle:(id)sender {
if(animating)
return; //it is already there
animating = YES;
JsonViewController *newsController = [(JsonViewController *)[self.newsNavController.viewControllers objectAtIndex:0]retain];
newsNavController.title = #"Parliament";
Item *currentItem = (Item *)[self.fetchedObjectsArray objectAtIndex:currentItemIndex];
NSString * urlString = [CONST_FEED_DISCRIPTION_URL stringByAppendingString:currentItem.guid];
[newsController initWithURLString:urlString date:currentItem.date];
[self.navigationController pushViewController:newsController animated:YES];
[newsController release];
}
This code takes a view controller that's already present in the navigation stack, reinitialises it, then pushes it onto the stack again. This doesn't seem right at all. You probably should be creating a new view controller. What's the background on this? What are you trying to achieve?
You are popping, change a title, than you are doing an initialization...
Decide if you will do deep clone of JsonViewController (alloc, init, copy field values) or just reference copy (retain). It will be mess later if you try to mix.
Your retain and release of the newsController object are unnecessary. That is why you are getting the warning. It looks like newsController is owned by the newsNavController object, which will retain it. The only reason you would need to retain newsController in this code is if you needed to use it outside the scope of this method. Since you don't need to retain it, you don't need to release it, hence the error. You may be assuming that the -initWithURLString:date: method is incrementing the retain count, but it is only new, alloc, and retain that do this. You should probably rename that method to not use the term init to avoid confusion.
What are you doing with -initWithURLString:date:??? Are you just adding a url? Then you should call it addURL...... If you really initialize it again, you set the pointer of the newsController variable to a new object. The first object it pointed to gets lost -> leak.
I assume you named the init method wrong and just add a url to a controller, which is already in the stack and add it again with a higher retain count, but still the very same object.
Don't do this. Copy the object or better - create a new instance of the viewController!!!

iPhone: How Memory works when adding to subview

A general question: When you add an item to an UIView, does that increase the owner count by 1? Does the main view that you added the item to now becomes an owner as well?
Example:
mainView = [[UIView alloc] init];
UILabel *label = [[UILabel alloc] init];
[mainView addSubview:label] //does this increase owner count by 1?
[label release] //and this decreases it by 1?
You release what you retain/init.
When you call addSubview:, it increases the retain count (or as you say owner count). But that increase belongs to mainView. So it is up to mainView to release the subview some point in the future, not you.
So when you init the label it increases the retain count to 1. When you call addSubview:label it increases the retain count by 1, to 2. Then you release the label, decreasing the retain count back to 1 and counteracting you're previous init.
Then when the label is removed from the mainView its retain count will go back down to 0 and it will be deallocated.
Never use the method retainCount, whether you're just observing it, or acting on it. This method will not display what you expect because of a lot of behind the scenes code. Just don't use retainCount.
Subviews are stored in a NSArray which sends a retain to every object added. In principle, yes the retain count goes as you expect but in reality you can never observe the retain count reliably because of all the retains and releases that occur behind the scenes in the API itself. Trying to track the retain count directly will just lead to grief.
It's better to just follow the rule of if you create an object with new or alloc-init then you release it. If you don't do the former, you don't do the later.

Scenes not completly released in Cocos2d iPhone application - debugging

I have an iPhone application in Cocos2d that sometimes crashes on actual device, due to memory problems.
What I have so far found out is that the scenes, when switched, aren't fully released - the [retainCount] for them is somewhat about 4-10 :)
The dealloc method never gets called, and then I assume, when I switch the scenes a few times, the memory issue shows up.
I'm wondering - where should I relese the scene? Since it has number of children, I suppose I should be doing a cleanup-removal of them. But it turns out that removing all children from the layer doesn't decrease the retain count of it. I added such a piece of code to my cleanup method:
- (void) cleanup {
while ([self.children count] > 0) {
CCLOG(#"child: %d - %# rc: %d", 0, [self.children objectAtIndex:0], [[self.children objectAtIndex:0] retainCount]);
[self removeChild:[self.children objectAtIndex:0] cleanup:YES];
}
[super cleanup];
}
But then the [self retainCount] method still returns a number greater then 1 or 0, and my dealloc doesn't get called.
Is there something I should be doing in order to release those children properly? If I add my own subclass of CCSprite as child, should I do something specific in that class' release or dealloc method, other then just calling it's [super] method?
Do not call retainCount
retainCount is useless, as you've discovered, when dealing with complex frameworks. There are any number of internal implementation details that could cause the retain count to be an unexpected value at any given time without indicating a bug.
You should release the scene to balance however many times you retained the scene, no more and no less.
If you release it more times than you retained it, you're app will likely crash whenever you [potentially accidentally] fix the real problem.
In general, when dealing with a hierarchy of items like views, layers, or sprites, you remove the root view/layer/sprite and that removal takes care of tearing down the hierarchy (including releasing as needed).
That assumes that you haven't retained anything in the hierarchy. If you have, then you need to also release those references when the root is removed and released.
Usually you don't have to release your children by yourself. How do you add your child?

Tricky memory management in objective-c

After reading:
Memory management of a view controller in Objective-c
and
Does UIView's addSubview really retain the view?
I wrote the following code to toggle a subview:
#synthesize switchableView, viewSelector, currentSubview;
//...
if(switchableView.subviews.count != 0)
[[switchableView.subviews objectAtIndex:0] removeFromSuperview]]
self.currentSubview = (veiwSelector.selectedSegmentIndex == 0) ?
[ViewA new] : [ViewB new];
[switchableView addSubview:currentSubview.view];
//[currentSubview release]; //<---crashes if I uncomment this line
It seems to run fine if I comment out that release line, but I can't wrap my head around why. Here's the way I understand what happens and maybe someone can tell me where I go wrong:
So lets consider currentView:
A gets alloc-ed by the 'new' message--retain count=A:1
A gets retained by the setter--retain count=A:2
A's view gets (supposedly) retained--retain count=A:2.1
next time through...
A's subview gets released count=A:2
B gets alloc-ed by the 'new' message--retain count=B:1, A:2
A gets autoreleased by the setter-- B:1, A:1
B gets retained by the setter--B:1, A:1
nothing ever gets rid of A?
So should I change my code, or am I wrong about the way memory management works in this language...or both?-
Ok, step one, ignore the retainCount. It's one of those things Apple should rename to something like lsdjiofsudfoiwjeriowhfiuwhrteiuhweifhsdjkfhsiurwoieuriosfho so people won't guess it's name, and not list it in the documentation. For your purposes, it's entirely useless, so ignore it.
Now that I've said that, let's consider something: addSubview: DOES retain its argument, and removeFromSuperview releases the receiver.
Finally, it's hard to tell what currentSubview is. It has a view property which would lean towards a VC, however, the way you're using it by itself, would indicate its a normal view. Perhaps you can clarify so I can continue my answer.
Your understanding of retain and release is correct, as is your code. That suggests that the problem lies outside of the code you've posted. For example, you would have this problem if your currentSubView property was defined as assign instead of retain.
You code is not structured well, however. This would be much clearer:
self.currentSubView = [[ViewA new] autorelease];
Furthermore, view controllers are meant to be cached, not created and released each time the user toggles a display. Typically, you create your view controllers beforehand, and access their .view property when necessary to display the view. UIViewController will automatically deallocate non-visible views in low memory conditions, and re-allocate their view when the .view property is accessed.
Change the release line to
self.currentSubview = nil;
and I think you'll be fine. You're releasing, but not setting the property to nil. So, when it gets re-assigned next time through, release will be called again on it. But you already released it so... boom.

Where I should call [object release]?

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