ViewController *vcObj = [[ViewController alloc]init];
[UIView transitionFromView:self.view toView:vcObj.view duration:2 options:UIViewAnimationOptionTransitionCurlUp completion:^(BOOL finished) {}];
[self release];
I am not releasing vcObj, if i release this app will crash and if i dont release this i get memory leak.
What is the standard way to do views transition or swaps between views?
i am new to this memory thing plz help me .. i studied books and tutorials but this situation i am unable to solve.
Release objects, as views or view controllers, in completion function. Animation is executed in another thread. If u release in Main Thread then objects can not exist when onvoked in the other.
Related
I have a login screen that I allocate in one place, and dismiss in another, and upon dismissal, its dealloc method is never called and the iVar holding the login screen still has a value even after being assigned nil in my dismissal code.
Here is my allocation
-(void)loginUser
{
loginScreen = [[LoginScreen alloc] initWithNibName:#"LoginScreen" bundle:nil];
[self.tabBarController addChildViewController:loginScreen];
[self.tabBarController.view addSubview:loginScreen.view];
[loginScreen didMoveToParentViewController:self.tabBarController];
[self.tabBarController.view bringSubviewToFront:loginScreen.view];
}
Here is my dismissal and deallocation in another method (which fails to deallocate)
[UIView animateWithDuration:0.4 delay:0.0 options:UIViewAnimationOptionCurveEaseIn animations:^{
loginScreen.view.frame = CGRectOffset(frame, -1024, 0);
}
completion:^(BOOL finished) {
// Remove the loginScreen
[loginScreen willMoveToParentViewController:nil];
[loginScreen.view removeFromSuperview];
[loginScreen removeFromParentViewController];
[loginScreen cleanupBeforeDealloc];
loginScreen = nil;
}];
I have some code that listens for keyboard notifications inside LoginScreen, but I added a method below to clean that up, and I tried calling it in my dismissal code above, but that still didn't fix it. grrrr.
-(void)cleanupBeforeDealloc
{
[self deregisterFromKeyboardNotifications];
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
I would suggest getting rid of loginScreen entirely. There is no need for it, as this is a child view controller and is accessible through your childViewControllers array. childViewControllers manages the retain and release for the child view controller, and your loginScreen property is adding an extra retain that could be messing things up.
However, the actual cause of the retain cycle is probably that the view controller has registered and retained an observer with the notification center. That is a common cause of retain cycles. The notification center retains the observer and the observer retains self. You cannot unregister in dealloc to break the cycle because the retain means that dealloc isn't called.
I have a parent UIView and an UITextView as one of the subviews.
And I created a button to dismiss the parent UIView like this:
-(void)cancelButtonPressed:(UIButton *)sender
{
[UIView animateWithDuration:0.2 delay:0.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
self.frame = CGRectZero;
} completion:^(BOOL finished) {
if (finished) {
[self removeFromSuperview];
}
}];
}
I can tell that the parent UIView didn't get released because if I typed some text into the UITextView and dismissed it, when I opened the UIView again, instead of a blank UITextView, the SAME text is in it again.
I checked the Leaks tool but I didn't see any leaking. So I'm guessing if I have some kind of retain cycle or what.
UPDATE:I have another object (which is the AppDelegate) who is holding the UIView's instance: _myView as a global variable like this:
_myView = [[MyView alloc] init];
_myView.nameLabel.text = _user.screen_name;
[_window addSubview:_myView];
[UIView animateWithDuration:0.2 delay:0.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
_myView.frame = CGRectZero;
} completion:nil];
But in order to avoid retain cycle, should I create a weak self like this: __weak MyView *weakSelf and in the animation block do this: [weakSelf removeFromSuperview]?
I've also tried calling removeFromSuperview on the view itself, and it doesn't result in the view being released.
If you want to release the view, then go with an approach that uses a delegate. That way, you will be able to call removeFromSuperview on the view, once the animation is complete, and set it to nil. This has worked for me in the past.
So, you can add a method to the view class that you want to animate closed, where you will do the animation. Set your view controller as a delegate to your view, and call some method on the delegate, from the completion block of that animation.
You can create your own protocol for this. If you keep it general enough, and focus only on animation callbacks, you can reuse the protocol in all your view controllers.
Memory management and logic are independent things. A memory leak will never change the behavior of your program. Behavior like displaying something is controlled by what you tell it to display. If it displays the same thing as before, then you must be giving it the same thing to display somehow. Even if you somehow leaked the original thing, if you pass a new thing for it to display, it will display that thing. So look at your logic. Memory management has nothing to do with what you're seeing.
I'm running into a problem that I'm hoping someone here can help with.
I'm writing an app that includes ViewController containment. The main controller swaps the various child controllers in and out as the user manipulates a SegmentedController.
It seems to work correctly, but I've found a vulnerability. If I select segments TOO QUICKLY, I can get the app to crash with the following error:
2012-01-19 04:29:39.539 MyApp[1057:fb03] * Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Children view controllers (ChildViewController1): 0x6e91480 and ChildViewController1: 0x6e8dca0 must have a common parent view controller when calling -[UIViewController transitionFromViewController:toViewController:duration:options:animations:completion:]'
It SEEMS that the issue is that I'm triggering an action while my animation is running (and the child controllers are being swapped in and out), and that that's the problem, but I'm not exactly sure how to PROTECT the UI from doing this while the animation is running. Any ideas would be greatly appreciated. Here's the code that's running the VC swap:
- (IBAction)selectPage:(id)sender {
NSLog(#"Page Selected");
UIViewController *newViewController = [[self patientChartViewControllers] objectAtIndex:[sender selectedSegmentIndex]];
[self addChildViewController:newViewController];
[[self navigationItem] setTitle:[newViewController title]];
[self transitionFromViewController:[self currentPatientChartViewController] toViewController:newViewController duration:0.50 options:UIViewAnimationOptionTransitionCrossDissolve
animations:^{
[[[self currentPatientChartViewController] view] removeFromSuperview];
newViewController.view.frame = self.view.bounds;
[[self view] addSubview:[newViewController view]];
}
completion:^(BOOL finished){
[newViewController didMoveToParentViewController:self];
[[self currentPatientChartViewController] removeFromParentViewController];
[self setCurrentPatientChartViewController:newViewController];
}];
}
I am not quite sure, but I think the transition among view controllers is handled by Core Animation framework and for some reason, Core Animation doesn't like doing multiple things while interacting with user -- all the same time.
To prevent this from happening, my understading is that, your SegmentedController (presumably an instance of UISegmentControl) stays in the view, while views are being exchanged. For the time that view are being exchanged, you can disable the user interaction of SegmentController so that users can not switch views-- they have to wait for the complete transition of view A to B.
Hope that helps.
I've tried using transitionFromViewController:toViewController:duration:options:animations:completion: with the flag check using completion block, but it seems completion block finishes before the animation, even if I set duration to 0.
If user is changing child view controller really fast, I think we should add and remove child view controller ourself, so as to avoid the animation. This works for me
Something like this
// Removing currentVC
[currentVC willMoveToParentViewController:nil];
[currentVC.view removeFromSuperview];
[currentVC removeFromParentViewController];
// Adding nextVC
[self addChildViewController:nextVC];
nextVC.view.frame = self.view.bounds;
[self.view addSubview:nextVC.view];
[nextVC didMoveToParentViewController:self];
self.currentVC = nextVC
I am struggling with understanding why the first method below works for hiding and removing a subview of a view. In this first method I pass the pointer by reference. In the second method, which is less general, I have a delegate method designed for removing a specific view. I would like to use the first method, because I have several views that I would like to apply this too. I should mention that the first method works without fail as long as it is called within the implementing class. It fails when I call it from the view controller that I wish to dismiss. I get an EXC_BAD_ACCESS on the removeFromSuperview line when it fails in the first method.
-(void)closeView:(UIViewController **)viewController
{
[UIView transitionWithView:self.view
duration:UINavigationControllerHideShowBarDuration
options:UIViewAnimationOptionCurveLinear
animations:^
{
[[*viewController view] setAlpha:0.0];
}
completion:^(BOOL finished)
{
[[*viewController view] removeFromSuperview];
[*viewController release], *viewController = nil;
}];
}
-(void)closeButtonClicked
{
[delegate closeView:&self];
}
//
// This method works without fail:
//
-(void)closeView
{
[UIView transitionWithView:self.view
duration:UINavigationControllerHideShowBarDuration
options:UIViewAnimationOptionCurveLinear
animations:^
{
// In this context viewController is defined in the class interface
[[viewController view] setAlpha:0.0];
}
completion:^(BOOL finished)
{
[[viewController view] removeFromSuperview];
[viewController release], viewController = nil;
}];
}
-(void)closeButtonClicked
{
[delegate closeView];
}
First of all, it is not according to the style guides, and not a good idea in general, to do a release of the viewController within a method like this. It will get you into trouble quickly. If the caller of this method is responsible for the viewController (it has done the retain), then it should release it as well. This is likely the cause of the first method not working from within the viewcontroller itself.
In the second method you do not pass in the viewController as parameter, which means it needs to be defined in the context.
If you don't release the viewController in this method, then you don't need to set its variable to nil either, and you can simply pass it as normal parameter:
-(void)closeView:(UIViewController *)viewController
{
[UIView transitionWithView:self.view
duration:UINavigationControllerHideShowBarDuration
options:UIViewAnimationOptionTransitionCrossDissolve
animations:^
{
[[viewController view] removeFromSuperview];
}
completion:nil];
}
you would then do this at the call-site:
[self closeView:childViewController];
[childViewController release]; childViewController = nil;
It safe to release the child in this way before the animation is done, because the animations block implicitly retains all objects referenced from the block, including the viewController parameter. Therefore, the child's dealloc is not called until the animations block releases it.
This does not work in your first code example, because you pass a pointer to a variable. That is, the animations block does not know it needs to retain the child.
BTW, I am not sure why you want to set the alpha, in the example above I show that you can also remove the view already in the animations block. See more about that in the UIView Class Reference.
**viewcontroller and &self is not the way to go. In Objective-C, you do [self.view removeFromSuperview] in the subview itself, in the parent viewcontroller you do release or with ARC just replace the subview with another view.
I'm trying to get my head around how object lifetime and reference counting interact with code blocks. In the following code I'm just doing a simple animation that flashes as the top view on a UINavigationController's stack is swapped. The tricky part is that the popped view controller is the one where this code is defined.
[UIView animateWithDuration:0.2
animations:^{self.navigationController.view.alpha = 0.0;}
completion:^(BOOL finished){
UINavigationController *navController = self.navigationController;
[self.navigationController popViewControllerAnimated:NO];
[navController pushViewController:nextView animated:NO];
[nextView release];
[UIView animateWithDuration:0.2
animations:^{navController.view.alpha = 1.0;}];
}];
My question is (ignoring what the animation looks like), is this the correct way to do this from a memory management perspective. In particular:
(1) When using this approach for the pop+push cycle, is it correct that it is no longer necessary to retain self, as in other similar examples that do not use blocks?
(2) Does invoking animateWithDuration:... with the these blocks retain the defining view controller (self) until the blocks execute?
(1) When using this approach the the pop+push cycle, is it correct that it is no longer necessary to retain self, as in other similar examples that do not use blocks?
It is correct. These blocks automatically retain self, navController and nextView if nextView is local variable.
(2) Does invoking animateWithDuration:... with the these blocks retain the defining view controller (self) until the blocks execute?
These blocks are copied to heap from stack by this method. And these blocks are released after execution. And then self, navController and nextView are released from these blocks.