Creating Table View in ViewDidAppear - iphone

I am creating a Table View in viewDidAppear, because my app requires it to be created here for multiple reasons. However I notice that I get a memory leak when I analyze my application.
I thought using the instance variable _tableView is not a good idea in any other method besides init and dealloc. Should I just use autorelease, I want to make sure the table gets released at the appropriate time.
There is a property for my Table View.
#property (nonatomic, retain) UITableView *tableView;
And I create the Table View as such:
- (void)viewDidAppear:(BOOL)animated
{
self.tableView = [[UITableView alloc]
initWithFrame:CGRectMake(0, 0, 320, 300)
style:UITableViewStyleGrouped];
// Table View properties
self.tableView.dataSource = self;
self.tableView.delegate = self;
[self.view addSubview:self.tableView];
}
- (void)viewDidDisappear:(BOOL)animated
{
self.tableView = nil;
}
- (void)dealloc
{
[_tableView release];
}

self.tableView is a retain property, so your synthesized setter increases the retain count. But when you create a new UITableView using alloc/init, you also increase the retain count. So this line results in tableView being retained twice:
self.tableView = [[UITableView alloc]
initWithFrame:CGRectMake(0, 0, 320, 300)
style:UITableViewStyleGrouped];
Once when you use alloc/init, and once when you call the synthesized setter using self.tableView =.
You do not have two corresponding release calls.
The proper way of handling this would be to autorelease the alloc/init'd UITableView object that you set self.tableView to, like so:
self.tableView = [[[UITableView alloc]
initWithFrame:CGRectMake(0, 0, 320, 300)
style:UITableViewStyleGrouped]
autorelease];
The rest of your code will work as expected.
As an aside, you probably don't want to create your UITableView in viewDidAppear. By that time your view has already appeared (hence the name), and you likely wanted your UITableView before that. You also probably don't want the CPU expense of creating a new UITableView every time the view appears. You probably want to create a UITableView in viewDidLoad and then reuse it, unless there is a really good reason not to.

Autorelease. You get one retain for the alloc and another for the property assignment (assuming it's a retained property).
Your dealloc should handle one and an autorelease the other.

Setting property is good, i set the property for instance variable when you want to use that variable in another class, otherwise it is not necessary ,
Since you are creating in viewDidAppear it will create always when the view appears for that controller so better relesase the tableview in ViewDidDisappear method.
If you use autorelease we wont get when it will be released so some times accessing tableview will cause crash of application,

On top of what the other guys have said, I'd discourage you from doing alloc in viewDidAppear because that method can be called multiple times depending upon what else goes on (and, by the way, cause leaks unless you start checking for the existence of a tableView before alloc'ing another). Doing it in viewDidLoad seems to be much safer.
As an aside, I believe that your viewDidAppear, viewDidDisappear and dealloc should all be calling their super versions, too.

Related

self removeFromSuperView don't release self?

I dynamically add a subview to another uiview;
in the subview ,click a button to go back by the following method:
[self removeFromSuperView ];
but after manny times added subview and removed it,my app could crash ,the log said it was killed .
I found that calling [self removeFromSuperView] didn't release self. So what's the best methods to releas it?
If you are retaining the UIView on creation (or adding it to an array) the retain count will increase. For example:
// Retain count is 1
UIView *myView = [[UIView alloc] initWithFrame:myFrame];
// Retain count is 2
[myParentView addSubview:myView];
// Retain count is 1 again
[myView removeFromSuperView];
In the above example you can autorelease the view if it is immediately added as a subView or release it in your dealloc if it is an iVar.
EDIT: (other reasons your view could be retained)
// Retain count +1
[myArray addObject:myView];
// Retained in the setter created by the #synthesize directive
#property(nonatomic, retain) UIView *myView;
Anything else that states in the documentation that the property is retained.
You should also be careful of creating objects in the loadView method of a VC, if you do make sure you release them, as they will be created again when the loadView is called. This will happen if you VC's view is unloaded and then reloaded.
u should release at first. counterpart of "alloc" is "release", and counterpart of "addSubview" is "removeFromSuperView":keep those balance.
add view:
UIView *myView = [[UIView alloc] initWithFrame:myFrame];
[myParentView addSubview:myView];
[myView release];
remove view (the view will clear up in memory after removeFromSuperView):
[myView removeFromSuperView];
Looks like you are adding retained view as a subview. Its parent view retains it once again.
So when you cell [self removeFromSuperView]; it gets release message from superView, but still have to be releasd by creator.

Programmatically-created subviews and viewDidUnload

For a view controller, any outlets that you set in Interface Builder must be released
and set to nil in viewDidUnload, and must also be released in dealloc.
(see: When should I release objects in viewDidUnload rather than in dealloc?)
One of the most important reasons for implementing [viewDidUnload] is that UIViewController subclasses commonly also contain owning references to various subviews in the view hierarchy. These properties could have been set through IBOutlets when loading from a nib, or programmatically inside loadView [emphasis added], for instance.
My question is, do we really need to implement viewDidUnload for subviews in the view hierarchy that are created programmatically in loadView (without Interface Builder)?
It depends how you created them and if you need to reference them elsewhere.
For example:
- (void)loadView
{
[super loadView];
UIButton *someButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
someButton.frame = CGRectMake(0, 0, 50, 50);
[self.view addSubview: someButton];
}
In the above case you would not need to implement viewDidUnload because someButton is autoreleased within loadView.
Another example:
- (void)loadView
{
[super loadView];
self.someButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
someButton.frame = CGRectMake(0, 0, 50, 50);
[self.view addSubview: someButton];
}
In this example you would want to use viewDidUnload, as you have another reference to someButton hanging around. You want viewDidUnload to release that button and reset the reference so you don't improperly use it as well as free up the memory. In this case you would also want to release the button in the dealloc method as well, in case viewDidUnload is never called.
You certainly should. If you look through Apple samples, however, you'll find that sometimes they just use dealloc. If you know your object will get deallocated at some reasonable time after use, I think this is perfectly reasonable. I follow this pattern, however, as viewDidUnload might not be called in certain exceptional cases. I do not actually call the release method such a long name:
-(void)releaseRetainedXibAndViewDidLoadObjects
{
self.myLabel = nil;
self.myImage = nil;
}
-(void)viewDidUnload
{
[super viewDidUnload];
[self releaseRetainedXibAndViewDidLoadObjects];
}
-(void)dealloc
{
self.myObject = nil;
[self releaseRetainedXibAndViewDidLoadObjects];
[super dealloc];
}
You could even do this from an application specific view controller object that your classes subclass to simplify matters.
Do it in your dealloc method. There is no guarantee that viewDidUnload will be invoked. It is typically only invoked when the controller needs to unload the view, e.g. when there's a memory warning. On the other hand, the init/dealloc pairing will always be invoked.

Won't reassigning a UITableViewController's tableView property cause a leak?

I created a subclass of UITableView and wanted to use it with a UITableViewController to get the benefits of auto-scrolling when the keyboard appears. In the loadView for my view controller (derived from UITableViewController) I did the following:
- (void)loadView
{
[super loadView];
self.tableView = [[MyCustomTableView alloc] initWithFrame:[UIScreen mainScreen].applicationFrame style:UITableViewStyleGrouped];
self.tableView.delegate = self;
self.tableView.dataSource = self;
}
Shouldn't this cause a leak with whatever self.tableView was referencing before the reassignment? I ran Build And Analyse and it didn't report it as a leak.
However, if I try to "be good"...
- (void)loadView
{
[super loadView];
[self.tableView release];
// reassign code...
}
...all sorts of nasty crashes happen when my view is displayed. Can anyone explain to me whether simple reassignment causes a leak and, if so, how to do this properly?
Thanks in advance.
No, it won't cause a leak because the setter method setTableView: (which is called when you assign a new value to the property) will automatically release the old value. This is what properties are for.
As Ole Begemann said, When you assign using dot notation, there's more happening than just a plain assignment. Since setTableView: is a "retain property", it looks something like this:
- (void)setTableView:(UITableView *)newTable
{
if(newTable != tableView) {
[tableView release];
tableView = [newTable retain];
}
}
So the problem in your "be good" method is the following.
If you just call release on the tableview object, you are not using the property setter, which means, you are not setting the tableView to nil. When you call self.tableView = [[MyCustomTableView alloc] init... the current tableView is pointing to a non-nil, already deallocated block of memory. This block of memory is the one that setTableView: will try to release again, raising a EXC_BAD_INSTRUCTION.
When you call release on some object the pointer of which may be reused in the future, always set it to nil afterward. This way you mark the pointer as deallocated and you won't risk to over-relese. Or just use a retain property, that will do the release for you when you assign it to nil.
Actually your example will cause a leak.
self.tableView = [[MyCustomTableView alloc] initWithFrame:[UIScreen mainScreen].applicationFrame style:UITableViewStyleGrouped];
Setting self.tableView will increase retain by 1, and the alloc will increase retain by 1.
When you later on set self.tableView to another value, retain count is decreased by 1, leaving 1, thus your allocated MyCustomTableView will never be released.
The reason your loadView crashes is because the initial table view that is there before you alloc and add your own, is properly set up, with 1 retain. So when you release it, the system will try to release it too but by then it's already deallocated, and nastiness ensues.
The proper way to do it is to (auto)release the allocated MyCustomTableView:
self.tableView = [[[MyCustomTableView alloc] initWithFrame:[UIScreen mainScreen].applicationFrame style:UITableViewStyleGrouped] autorelease];
That way, it will have 1 retain count only, which will drop to zero when you (re)set self.tableView.

iPhone - Memory Management problems

I am going over my code and trying to get a handle on proper memory management. This code:
imageView = [[[UIImageView alloc] initWithImage:myImage] autorelease];
Causes my application to crash. I am using multiple view controllers within a nav bar controller. The app works fine: I can select a person from the first view controller (tableview) and it puts me to a list of that persons photos, I can then select a photo from that view controller (tableview) and move to a final view with a scrollview for viewing the photo. Once I hit back on the navbar the previous view loads (list of photos in a tableview) however the app crashes right before the row is deselected using this code:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
if(RowSelected != nil)
{
[MainTableView deselectRowAtIndexPath:RowSelected animated:YES];
}
}
The selected row is stored when a the user clicks a row.
If I leave the code as:
imageView = [[UIImageView alloc] initWithImage:myImage];
The app runs fine. Am I doing something wrong? Do I not need to autorelease this?
Make sure you create that view in your view controller's -loadView or -viewDidLoad, not in its initializer. When the controller's view goes offscreen it usually gets released, which in turn releases its subviews; thus, you should not expect your reference to imageView to remain valid. If you for some reason need the image view to stay in memory even when the view controller's offscreen, then it's okay not to call -autorelease when you create it; just make sure to call [imageView release]; in your controller's -dealloc.
When you mark imageView for autorelease, it will be released the next time thru the run loop. If you still are referencing or using imageView somewhere then you are using a pointer to heap space that has been released. The heap space will get overwritten (sooner or later) and you will be referencing garbage and crash.
I think the correct solution is that imageView should be a property that is retained, but I'm not sure what you are doing with imageView so I'm only guesstimating here. If you add imageView to your view Controllers view it will retain it in the subviews array. Bottom line, it has to be retained by whoever is using it.
You would make imageView a retained property in the .h file:
#property (nonatomic, retain) UIImageView* imageView;
Then use the setter, which will retain it:
UIImageView* tmpIV = [[UIImageView alloc] initWithImage:myImage];
self.imageView = tmpIV; // copy the pointer, setter will retain it
[tmpIV release]; // release the original alloc
Don't forget the #synthesize to create the setter/getter.
The ImageView is in my .h. I am using this line it in my -viewDidLoad, i do not need the view once it goes offscreen:
imageView = [[[UIImageView alloc] initWithImage:myImage] autorelease];
I think that what i am doing is assigning the allocated UIImageView to my imageView and if i auto release then the retaincount will be off because my -dealloc releases? (corret me if im wrong)
if i check my -dealloc:
- (void)dealloc {
NSLog(#"%i", [photo retainCount]);
[photo release];
NSLog(#"%i", [imageView retainCount]);
[imageView release];
[super dealloc];
}
The retaincount of the imageView is 1 in NSLog, im guessing that when it goes to do [super dealloc] that it tries to take 1 more retain count off of the imageView when it is 0 already because of the [imageView release] ?
if i have the line: imageView = [[UIImageView alloc] initWithImage:myImage];
then the retain count is 2 in NSLog, 1 going into the [supper dealloc]
if i use kk6yb's method, then the app works no problems, however i think i shouldn't have to do it this way because using his way if i check my retain count in dealloc it is also 2 in the NSLog...
I am confused, i read alot about memory management yesterday to get a better grip on things, since i am releasing the imageView in the -dealloc i believe i do not need to autorelease on that line?
Is there a way to check memory leaks in xCode?
Thanks!

UIViewController: set self.view to my view or add my view as subview?

I have a question about UIViewController's subview, I created a UIView subclass MainView, which has the exact size of the screen, I wonder which is a better way of adding MainView, consider the following factors:
1 As MainView has same size as the whole screen, the MainView itself may have subviews, but there is no views at the save level as MainView(ie I don't need to add other subviews to self.view).
2 If I use self.view = mainView, do I put the code in loadView(as the viewDidLoad method means the view(self.view) is already loaded)? I see the loadView method is commented out by default, if I add the code to this method, what other code do I need to put together(e.g. initialize other aspects of the application)?
3 If I add mainView via [self addSubview:mainView], are there actually two off screen buffer? One for self.view, one for mainView, both has same size as the screen and one is layered on top of the other(so it wastes memory)?
Thanks a lot!
I'm not sure I completely understand what you're asking, but I'll try to answer a few of the questions you have.
First of all, if you have multiple UIViews on the screen they are all loaded into memory. You have to do -removeFromSuperview and release them to get the memory back.
You can assign your UIView as the UIViewController's view. For example:
MainView *mainView = [[MainView alloc] initWithFrame:CGRectMake(320.0, 480.0)];
self.view = mainView;
[mainView release]; //since the .view property is a retained property
in that case, you have have the view's initialization code in the -init method. Just redefine it like:
- (id)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
//initializations
}
return self;
}
You must implement loadView if you did initialize your view controller with a NIB.
UIViewController takes care of sizing its "main" view appropriately. This is all you need to do:
- (void)loadView
{
UIView* mainView = [[[UIView alloc] initWithFrame:CGRectZero] autorelease];
self.view = mainView;
}
I'd solve all of this by doing it in a xib! If you create a UIView in your xib, you can then change it's class (when you select the UIView there should be a text field in the Class Identity section of the Identity inspector* - type 'MainView' here!)
Then, create your view controller by calling
myViewController = [[MainViewController alloc] initWithNibName:#"MyNibName" bundle:nil];
That should solve your problems; it's the main subview of your view controller (directly accessable from self.view) and you don't need to worry about memory usage, there's only one view :)
Sam
NB * Click tools -> Identity Inspector. I didn't know it was called this until I had to write this answer!
Yes, the first code-snippet shown above is the "standard" approach, AFAIK, when not using (evil!) NIB files -- i.e. when alloc'ing your view in-code, via loadView.
Note it seems one can also get away with the following, instead of hard-coding the screen-rect size:
UIView *myView = [[UIView alloc] initWithFrame:[UIScreen mainScreen].applicationFrame];
self.view = myView;
[myView release];
Note you definitely want to do the [myView release] call since, indeed, as pointed out above, self.view (for UIView) is a retained property.
Cheers, -dk
Perhaps the most important thing to do is make sure you have the following:
self.view.userInteractionEnabled = YES;
While it might not be required all of the time, it fixes the issue where self.view is unresponsive. This issue pops up occasionally.