How to avoid crashes on call back to deallocated delegate objects - ios

My application is on ARC and I still see few crashes. I then saw that I was setting my view controller as delegate of an alert view and then when the alert is on screen, on tap on "OK" button I was moving back to previous view and my current view controller was getting dealloc-ed. After it is getting dealloc-ed, I got a call from UIKit for the alert view and it crashes. As a safe handling, I created a property for UIAlertView and in the dealloc now, I am setting delegate of this UIAlertView to nil. This is working fine now.
I see another crash happening randomly:
-[CFString release]: message sent to deallocated instance 0xd2de900
My question here is that there are lot many places where I create local instances of some objects (custom view controllers or iOS objects like UIAlertView) and set my view controller as delegate of it. Do I need to create class level properties for all of them and the delegate on them to nil in the dealloc? Is there any other easy alternative to make sure no call back happens after delegate object is gone.

Related

dismissViewControllerAnimated sends message to zombie object

I have two UIViewControllers, VC1 and VC2. VC1 has a button that invokes a triggered Modal segue to VC2. This segue is defined in the storyboard. In VC2, the user can return to VC1 using a pan gesture that executes this line of code:
[self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
This code has worked for a long time. However, all of a sudden when this line runs, the app crashes. I enabled zombie objects and I can see this error:
-[VC2 retain]: message sent to deallocated instance 0x7f843a81e200
I've run the zombie profiler and here are the results:
UIClassSwapper initWithCoder seems to be where the app crashes, and there appears to be a an over retained object there, but I can't get any further. When I set an exception breakpoint, I just get into the assembly code, and this isn't of any apparent help.
So, there are a couple of questions here. The first is simply what am I doing wrong in how I am presenting and dismissing this view controller? After that, I don't know how to crack into the event history to figure out how to handle this zombie object.
Any help is appreciated. Thank you!
It seems that you use ARC project. Please provide more code:
1. How you initialize presentingViewController property (and declare). Is it (nonatomic,strong)?
2. How do you put this ViewController to the screen. Is it a kind of modal view controller etc?
-- added after comment.
ARC based project decides automatically if the object is needed or not and removes it from the stack automatically. So, the only one useful recipe to avoid this problem is to add NSNotification observer and post there notifications time after time. It will keep view controller in the stack and avoid automatic release.
Another solution - check if the controller is still in the stack:
if (!self.presentingViewController) [[THECONTROLLER alloc] init];
The third one - is to use some object to declare VC1 and VC2 as class variables, and present them as modal view controller when needed. In this case, object and its variables will be kept untouched as long as any of VCs presented.
In any case, the Exception happens because ARC considers that VC is not needed anymore and releases it atomatically.

[ViewController gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:]: message sent to deallocated instance

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.

How to isolate the cause of strange iOS app crash

There's a crash in the app that I'm working on that I'm having a difficult time tracking down the cause of. Here is the only set of events which causes the crash:
The app opens with the main view controller (VCmain) being presented. A button is triggered which opens a modal view controller (VCmodalA) via a segue. VCmodalA simply displays some information gathered about a core data object. VCmodalA is then dismissed by a button press. At some future point another view controller is presented modally (VCmodalB), which has some fields for the creation of a new core data object. After the object is created, if VCmodalB is dismissed, the app crashes with the following error:
*** -[UILabel _supportsContentDimensionVariables]: message sent to deallocated instance 0x818e200
If VCmodalA is not displayed prior to VCmodalB, or a new core data object is not created, or VCmodalB is not dismissed, then no crash occurs. I am at a loss what '_supportsContentDimensionVariables' means or who is sending it, although it appears to be sent to a label which was on VCmodalB (therefore the crash must be caused because a call is being made to a label that was deallocated when the view controller was dismissed).
I've spent hours poking around in Instruments looking at the Zombie left behind and trying to isolate the offending code by commenting it out, but I have been totally unsuccessful. At this point any hints would be welcome!!
Thanks so much!
There are two top causes of these kinds of errors: failure to use ARC, and direct access of ivars (particularly if you're not using ARC). Fixing those two issues is the best way to avoid these kinds of crashes.
As for how to debug it, first, you want to audit your accesses to UILabel objects. If you have any ivars that point to UILabel they should be strong or weak, never assign.
You should make sure that view controllers don't run code when they're not onscreen. This crash makes me think that this is a likely problem. For example, do not setup timers in viewDidLoad or initWithFrame:. Set them up in viewDidAppear: and tear them down in viewWillDisappear:. Similarly for KVO or delegation. View controllers manage views; if they're doing something when their view is not onscreen, your design is incorrect.

Crash when UIAlertView dismissed

I am using ARC.
I show an alert view after a user taps a UITableView cell.
99% of the times everything works great.
NOW TO THE WEIRED PART
I have this view hierarchy -
UITableView --> First UIViewController --> Child UIViewControllerControllers
The crash only appears after I open one, specific child UIViewControllers. After I enter all the other child viewControllers the alert view works great. If I enter the "problematic" view controller even once, The UIAlert view will crash even 5 minutes later and even if I get into all the other view controllers.
I have to say again that the alertView is presented in the root UITableView. And that it crashes even if the alert view callback method is only NSLoging.
The crash report is:
*** -[ReviewViewController isKindOfClass:]: message sent to deallocated instance 0x20bea8d0
while 'ReviewViewController' is the problematic viewController.
Thanks
Shani
You're setting the delegate of the UIAlertview to the calling UIViewController. That UIViewController is being de-allocated in certain cases when you navigate away from it.
If you require a delegate to respond to the UIAlertView, you'll have to structure your code so that the delegate doesn't get de-allocated before the UIAlertView will be dismissed.

alertView:didDismissWithButtonIndex: message sent to deallocated instance

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.

Resources