I have a view which is slideshow settings, and another view which has a slideshow. Unfortunately, these views conflict because they are still in memory.
As far as I know, viewDidUnload and dealloc are only called in low-memory situations, and dealloc should not be called directly, so how do I completely remove a view. These views are within uinavigationcontrollers by the way.
If you've added a view with UINavigationController remove it with PopViewController.
Pseudocode:
UIView *newView = [[UIView alloc] init]; // retain = 1
[UINavigationController pushView: newView]; // retain = 2
[newView release]; //retain = 1
[UINavigationController popView]; //retain = 0, object will get destroyed
Related
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.
It doesn't seem like
transitionFromView:toView:duration:options:completion:
Handles memory like it indicates in the Apple docs. It is stated that the fromView is removed from the superview (implying a release) and the toView is added to the superview (implying a retain). Is this correct?
When I transition a view, later in my app when the view is actually presented I will get a BAD_ACCESS as the view was deallocated.
Any ideas? Thanks!
UPDATE:
Here is the code where the problem exists:
UIViewController *container = [[UIViewController alloc] init];
container.view.bounds = [UIScreen mainScreen].bounds;
[container.view setBackgroundColor:[UIColor blackColor]];
/* Deallocated in the finish callback */
tutorialViewController = [[TutorialViewController alloc]
initWithNibName:#"TutorialViewController"
bundle:nil];
tutorialViewController.tutorialDelegate = self;
[tutorialViewController loadTutorialData:data];
UINavigationController *nc = [[UINavigationController alloc]
initWithRootViewController:tutorialViewController];
nc.navigationBar.barStyle = UIBarStyleBlackOpaque;
[UIView transitionFromView:[[window subviews] objectAtIndex:0]
toView:container.view
duration:kAnimationDuration
options:UIViewAnimationOptionTransitionCurlUp
completion:nil];
[container presentModalViewController:nc animated:NO];
[container release];
[nc release];
If I do a [tutorialViewController release] at the bottom of this method, I will get the BAD_ACCESS. So it seems like the UINavigationController does not retain the root view controller.
P.S. The tutorialViewController was not a member variable previously, but I have now fixed this problem by simply releasing it after the view has been dismissed.
You should check and confirm that you are not releasing the view yourself, hereby over-releasing. Or perhaps check the dealloc method for the view class which is being released to see if you're over-releasing anything in it.
The answer, after much testing, is simply that UINavigationViewController does not retain the view with initWithRootViewController. The Apple docs aren't clear on this.
Should I release my subviews of UIView in the viewDidUnload when I have references to them as instance variables which retains them? I have build the GUI programmatically. I should do that right? Since both uiview and ivars retain then the objects would have 2 in retain-count, when view receives e.g. memory-warning then the UIView will release the subviews, but they still have +1 in retain count so I have to setself.myIvar = nil; In the viewDidUnload?
Thanks for your time.
You actually can release all retained subviews in viewDidUnload. But I used to do it in another way:
-(void) viewDidLoad {
someInstanceView1 = [[UIView alloc] init];
[self.view addSubview: someInstanceView1];
[someInstanceView1 release];
someInstanceView2 = [[UIView alloc] init];
[self.view addSubview: someInstanceView2];
[someInstanceView2 release];
//etc...
//you have a references to someInstanceView1 and someInstanceView2 with retained counts 1
}
In this case even if memory warning will arise, the view controller will remove all it's view subviews. And then call viewDidLoad again. So there would be no leaks and you don't need to care about releasing that ivars at all cause the only owner (it has the strong reference to the views) is the view controller's view and it will release them automatically.
I have created a view and assigned the view to viewcontroller
UIView *newView=[[UIView alloc] initWithFrame:CGRectMake(0,0,320,460)];
in viewDidLoad method I have assigned the view to the viewcontroller
-(void)viewDidLoad{
self.view=newView;
//[view release]; In this case also the application crashing
}
-(void)dealloc{
[newView release];//IN this case also the application crashing.
[super dealloc];
}
The crash log is this.
how to release the newView? or else the viewcontroller itself will take care of releasing the newView.
In most circumstances you will do the following:
- (void) loadView {
// Don't call super here if you assign the view property
UIView *newView = [[UIView alloc] initWithFrame: [UIScreen mainScreen].applicationFrame];
self.view = newView;
[newView release];
// the view is now owned and retained by the UIViewController
}
Apple recommends you allocate the view, assign and release it in -loadView and don't worry about it in -dealloc. This is important in cases when your view may be purged under low memory conditions.
I'm building a segmented control within my viewDidLoad method, like so:
NSArray *tabitems = [NSArray arrayWithObjects:#"ONE", #"TWO", nil];
UISegmentedControl *tabs = [[UISegmentedControl alloc] initWithItems:tabitems];
tabs.segmentedControlStyle = UISegmentedControlStyleBar;
tabs.frame = CGRectMake(185.0, 7.0, 130.0, 30.0);
tabs.selectedSegmentIndex = 0;
[self.navigationController.navigationBar addSubview:tabs];
[tabs release];
But when the user goes Back in the uinavigationcontroller hierarchy, the segmented controller stays on the navigation bar. How would I get rid of it? Or am I doing something fundamentally wrong?
EDIT
Following Alex's suggestions, I propertized tabs and tried:
NSArray *tabItems = [NSArray arrayWithObjects:#"FAQs", #"Terms", nil];
self.tabs = [[UISegmentedControl alloc] initWithItems:tabItems];
but I'm not sure it's a good idea to alloc a property;
And I'm using
[self.tabs removeFromSuperview];
in my viewWillDisappear. Is that enough?
Retain a reference to the segmented control in your view controller (i.e define tabs as a property in the view controller's header file).
Override the view controller's -viewWillDisappear: method, and remove the segmented control from the navigation bar there, using the control's -removeFromSuperview method.
EDIT
You would still alloc-init your segmented control tabs in -viewDidLoad. You just need to set up a retain property for tabs in your view controller's header, and move the control's release statement to the view controller's dealloc method.
Read the "Properties" section of this Objective-C tutorial for an introduction to properties and how to set them up.
The way to override a method is as follows:
- (ReturnClass) methodNameToOverride:args {
[super methodNameToOverride:args];
// your code goes here...
}
In the case of -viewWillDisappear:, this method gets called when your view controller is about to disappear, such as when it gets popped off the navigation stack. This is a great place to put code that manages clean-up of view-controller-specific items, like your segmented control:
- (void) viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[tabs removeFromSuperview];
}
EDIT 2
If your property is set as follows:
#property (nonatomic, retain) UISegmentedControl *tabs;
then you are going to retain anything you set self.tabs equal to.
Your code here:
self.tabs = [[UISegmentedControl alloc] initWithItems:...];
will create a memory leak, because you are retaining this object: [[UISegmentedControl alloc] init] — but you never release [[UISegmentedControl alloc] init] itself. This is bad.
Instead, use autorelease on the right side, i.e.:
self.tabs = [[[UISegmentedControl alloc] initWithItems:...] autorelease];
The tabs property retains its own reference to the initialized segmented control. That initialized segmented control is itself released properly at some later point. So no more memory leak.
Even better, set the UISegmentedControl's owning UIViewController's navigationItem.titleView to the UISegmentedControl.
-(void) viewDidLoad {
if(!mySegmentedControl) {
// initialize the UISegmentedControl
...
}
self.navigationItem.titleView = mySegmentedControl;
}
No manual removal of the UISegmentedControl or any other UIView for that matter required except, of course, releasing when owning UIViewController is dealloc'ed. Also, to be a "good memory citizen", you might set your property to nil in -viewDidUnload.
You can also customize the left and right barButtonItems of the UIViewController's navgiationItem.