Reference Counting of self in Blocks - ios

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.

Related

How can I deallocate my view controller?

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.

Sending removeFromSuperview to self didn't release itself in my scenario

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.

ARC + Dealloc is not called

I am little confuse because my dealloc is not called in ARC. I have using storyboard in my application.
Case 1: Mydealloc called when i use all IBOutlet from storyboard
Case 2: My dealloc is not called when i try to use alloc and init methods in UIViewController. such as below.
UIViewController *vc = [[UIViewController alloc] initWithNibName:#"ProfileDetailView" bundle:nil];
__weak ProfileDetailView *detailview = (ProfileDetailView *)vc.view;
detailview.backgroundColor = [UIColor clearColor];
vc = nil;
....Set value in object.....
[self.view addSubview:detailview];;
detailview = nil;
Can you explain why dealloc is not called? and How can i able to achieve to call dealloc?
Thanks
The concept of ARC is that an object's retain count should theoretically be 1 in order for it to be deallocated. When you execute:
[self.view addSubview:detailview];;
Your self.view increments detailview's retain count by 1 when it adds it to view.subviews. Logically, when self.view removes detailview, the retain count is decremented by 1.
Again, this is a theoretical perspective. The reality is usually:
INSANE.
No one really knows how the mysterious Objective-C runtime works! (just kidding the whole source code is available online.)
Thanks for your reply. I am able to called my dealloc function when view controller pop. For achieving that, we need to remove my added subviews from my view when user tapped on back button.

Removing view from superview iOS

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.

transitionFromView memory issue of releasing newViewController and oldViewController

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.

Resources