I have a simple scenario.
I push myViewController onto navigation stack.
myViewController is basically showing a collection view over entire screen. I added an additional UIPanGestureRecognizer on this collection view and set myViewController as its delegate. I am retaining a strong reference to that pan gesture recognizer inside myViewController.
When I tap Back, myViewController gets popped from the navigation stack and deallocated. The myViewController's dealloc method gets called as it should. Up to this point everything works as expected.
Then I try to open the same myViewController like the first time and the crash occurs with the message:
[MyViewController gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:]: message sent to deallocated instance
I have this method implemented in myViewController and it always returns YES. But this shouldn't even matter because no one should even be calling this method because none should have a strong reference to it. Obviously someone is still holding a weak reference since the dealloc method was called on the only instance that ever existed.
Not even the init method of the MyViewController gets called.
I tried to put the following code both in dealloc and in viewWillDisappear:
[self.myPanGestureRecognizer removeTarget:self action:#selector(panGestureAction:)];
[self.collectionView removeGestureRecognizer:self.myPanGestureRecognizer];
self.myPanGestureRecognizer.delegate = nil;
self.myPanGestureRecognizer = nil;
But, it didn't change anything. Every time the same thing - myViewController gets initialized and displayed normally the first time. The second time I try to initialize and push, the exception occurs. Obviously, it is related to the pan gesture recognizer that I added, but I don't see how.
The answer to this question ended up fixing my issue that was very similar:
gestureRecognizer shouldReceiveTouch persisting in deallocated view causing crash
I was incorrectly setting self.navigationController.interactivePopGestureRecognizer.delegate to self.
So even though the error reported from the NSZombie was in another class. It's gesture recognizer was not actually the culprit, it was my interactivePopGestureRecognizer.
Related
Say if I have a navigation stack like this: UINavigationController->ViewController1->ViewController2. When ViewController2 gets popped, does the system guarantee the executing order of below methods?
dealloc of ViewController2
viewWillAppear of ViewController1
I've tested for a while and only see dealloc is called first, but I don't know if this can be guaranteed, i.e., is it also possible ViewController1's viewWillAppear gets called before ViewController2's dealloc?
There is no such guarantee of order, and it's even likely viewWillAppear of your ViewController1 is called before ViewController2.dealloc is called.
The reason is that depending on autorelease pools and internal references, it's likely the system still has a reference to the view controller that is just disappearing/has just disappeared when it calls the viewWillAppear of the view controller that's about to get shown.
If you require cleanup to be run when ViewController2 is popped, implement viewWillDisappear/viewDidDisappear. Again, there is no guarantee that vc2.viewWillDisappear will be called before vc1.viewWillAppear, but you can rely on vc2.viewWillDisappear getting called before vc1.viewDidAppear (note Did, not Will in the last one).
The problem is only happening with iOS4.3. I'm using ARC and my Base SDK is iOS6.
In -viewDidAppear of my view controller, I check if this is the first time the app has been started and if so, then I create and show a UIAlertView. I assign that UIAlertView to a strong property on the view controller and set self as the UIAlertView delegate.
self.uiAlertView = [[UIAlertView alloc] initWithTitle:#"Welcome!"
message:messageString
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:#"View Tutorial Videos", #"Email Support", nil];
When I tap one of the buttons, the app crashes complaining that -alertView:didDismissWithButtonIndex: was sent to a deallocated instance. The delegate is the view controller that is displaying the UIAlertView.
On all subsequent launches of the app, when the UIAlertView isn't shown there are no problems. The view controller is definitely not being deallocated.
If I display the UIAlertView but set the delegate to nil, then there is no problem and the app continues working, so clearly the view controller hasn't been deallocated because I can keep using it.
What is happening? This only causes a problem with iOS4.3.
EDIT: Based on suggestions in the comments, I added some more log messages in different places.
I've discovered that the view controller IS getting dealloc'd, but only if that view controller displays the UIAlertView. What in the world would cause the view controller to get dealloc'd just because it sets itself as the delegate of a UIAlertView and then displays it?
My app delegate has a strong reference to the view controller, so there is absolutely no reason that I can see for the view controller to get dealloc'd.
EDIT 2: I've discovered that during start up my main view controller is being instantiated TWICE. The first one is the one creating the UIAlertView and that one is getting dealloc'd. The second one is the one that I've been able to interact with afterwards that made me think the view controller was still there and operable.
However, I can't figure out where or why my view controller would be created twice. I don't have any alloc/init statements for the view controller. It only exists in the MainWindow_iPhone.xib.
The first time viewDidLoad is called on my view controller, the stack frame above is [UIViewController view]. The second time viewDidLoad is called on the second instance of my view controller, the stack frame above is [UINib instantiateWithOwner:options:]
EDIT 3: I've "fixed" the problem, but I don't understand why this would happen. Perhaps you can help me understand.
In my MainWindow_iPhone.xib, I created my root view controller and assigned it to an IBOutlet on my app delegate. Instead, I deleted the view controller from the xib and created it in code in the -application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions ... and the problem disappeared.
Why in the world would the view controller be created twice when in the xib?
I have this issue before. alertView:didDismissWithButtonIndex: is called after alertView: clickedButtonAtIndex:. You most likely deallocate the view controller in alertView:clickedButtonAtIndex by doing something like [self.navigationController popViewControllerAnimated:YES].
UIAlertView delegate is assign not weak reference. When delegate is deallocated, it is not set to nil automatically. That's reason why your code is crashed.
i fixed this issue commenting this method (or deleting it).
- (void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex
{
NSLog(#"se apreto cancel");
}
as James Wang said, the didDismissWithButtonIndex is called after clickedButtonAtIndex so i commented it to avoid the crash.
I have a MyViewController, it's based on UIViewController, and I used it like the following code:
MyViewController *nextViewController = [[MyViewController alloc] init];
[self.navigationController pushViewController:nextViewController animated:YES];
[nextViewController release];
And in the MyViewController, with a user event, have the following code:
[self.navigationController popViewControllerAnimated:YES];
Now, I find that, the MyViewController's dealloc don't be called, but, when I switch the App to background, for example, pass the home button, the dealloc method has been called! This a big problem! There will be got a lot of MyViewController wouldn't be release, when user go to a MyViewController, and go back, again and again, and just, the lots of memory could be release only when the App goto background.
So, can anyone help me about this, thanks!
The obvious reason is that something is retaining your viewController. You will have to look closely at your code. Do you do anything that in your class that uses delegates, since they sometimes retain the delegate. NSURLConnection will retain your class, and so does NSTimer. You can scatter code in you class and log your class's retain count, and try to find out where. In the code you showed so far the retain could should just be 1, since the class is only retained by the navigation controller.
Also, before you pop your view, get a reference to it, pop it with NO animation, and then send it some message that has it report the retain count (this would be some new method you write). That new method could also log other things, like whether it has any timers going, NSURLConnections, etc.
I have a custom UIViewController subclass that handles all the view initialization by itself (it doesn't use nib). There is also another UIViewController subclass loaded from nib. Both are contained by UITabBarController.
When a memory warning comes, the first controller does receive notification, but viewDidUnload doesn't get called. The second controller also receives notification and it's viewDidUnload does get called.
I checked in didReceiveMemoryWarning, self.isViewLoaded is TRUE and self.view.superview is null.
Both controllers (their tabs) are invisible at the time the notification appears.
Is there something special a custom view controller should do to be unloaded in a result of memory warnings?
If you are subclassing UIViewContoller and you do not initialize it from a NIB, you need to subclass the -loadView method. Otherwise iOS assumes that the view cannot be unloaded / reloaded.
It would be sufficient to just add the following to your implementation:
- (void)loadView {
[super loadView];
}
I will try to find a documentation quote for that.
The documentation is unfortunately not very clear on this matter.
I would check out the documentation on the View Controller Lifecycle. Specifically, check out the section on what happens when the memory warning is received. If your custom view controller's view is the view on screen, the OS will not attempt to release this view. Is this view on screen when you're getting the memory warning? In the simulator, navigate to the nib-loaded view and simulate a memory warning, see if your custom view gets released then. Also, see if viewWillUnload is being called. And make sure that in any of these methods that you're overriding that you call super.
I have a viewController called "FirstViewController". In an IBAction i call another ViewController called "thePageFlipViewController" and push it in sight via
[self presentModalViewController:thePageFlipViewController animated:YES];
after some time the user closes thePageFlipViewController with a button where the following code is executed via a delegate in FirstViewController:
[self dismissModalViewControllerAnimated:YES];
[thePageFlipViewController release];
And here is my problem:
-viewDidLoadin FirstViewController get's sometimes called after dismissing thePageFlipController. I don't understand why, because firstViewController should live in background. Is it dependent how long the modal view is displayed? is it possible that ARC does release something?
My problem is, that i initialise a lot of objects in viewDidLoad and the app crashes if viewDidLoad gets called again. I define some Routes for RESTKit there and RestKit complains that the routes are already set up and crash the app.
Any Help is appreciated.
When a view is not actually displayed it can be unloaded to free up memory. You would get a call to viewDidUnload: when that happens so you can release any objects you are holding strong references to. Then next time the view is needed, viewDidLoad: will get called again when the view is reloaded, there you have to recreate the objects you released in viewDidUnload:.
See the Memory Management section of the UIViewController class reference.
Also this answer has a good explanation already.