95
votes

Ok, here is the deal, I hate putting out questions about my debugging and crashes. Because I usually handle them myself, but I just cannot get my way around this, even after viewing multiple questions already.

Ok so here is the problem, I find my app randomly on and off crashing with this stack trace:

*** -[ViewController respondsToSelector:]: message sent to deallocated instance 0x1e5d2ef0

Where ViewController can vary, sometimes the place where my code crashes, has NO relevance to that particular ViewController and doesn't own or call it.

Also, to get that console trace, I have enabled Zombies, otherwise I would get no console print at all, I would only get: objc_msgSend, which I know means I am messaging something that is released. But I cannot find where that is... I am really stuck! Usually I always debug my crashes, so I am really stuck on this.

Again, this crashes in different places at different times, on and off. And the place it crashes has almost no relevance to the ViewController. And I find this very confusing.

Do you need any of my code? I have a lot of files and since it is crashing in different places, distributing my code will be a mess!

I have tried to add symbolic breakpoints with no luck, and Zombies is not available on the Instruments application for iOS. I cannot run my app on the simulator as it has unsupportive architecture frameworks for it.

Thanks everyone...

7
did you look at this question:stackoverflow.com/questions/1585688/…self
Assuming the way you transition to your views is consistent, maybe you can show us an example or two. If you're doing standard push/presentViewController calls you should be fine, but I see a lot of people here doing things like alloc/init'ing a view controller, but then not doing a push/present, but rather just adding the controller's view as a subview. Just a random example. But we can't diagnose this without some code. Hopefully a few snippets will help us figure out what's going on, so let's see.Rob
How about enabling symbolic breakpoints? Try adding these: wiki.zemingo.com/index.php?title=Symbolic_BreakpointsStavash
@RobertRyan I use presentModalViewController, I do not add it as a subviewMCKapur
In my case, my child view controller contained a webView, and the child VC was the delegate for the webView's scrollView. I needed to manually remove the delegate reference during dealloc/viewWillDisappear or I got this crash. Hope it helps someone.Dermot

7 Answers

169
votes

Use Instruments to track down deallocated instance errors. Profile your application (Cmd ⌘+I) and choose Zombies template. After your application is running, try to crash it. You should get something like that:

enter image description here

Click on the arrow next to address in the popover to show object that was called after it was deallocated.

enter image description here

You should see now every call that has changed retain count of this object. This could be because sending directly retain/release messages as well as draining autorelease pools or inserting into NSArrays.

RefCt column shows retainCount after action was invoked and Responsible Caller shows class name and method in which it was performed. When you double click on any retain/release, instruments will show you line of code where this was performed (If this isn't working, you can examine call by selecting it and choosing its counterpart in Extended Detail pane):

enter image description here

This will let you examine all the retainCount lifecycle of object and probably you'll find your problem right away. All you got to do is find missing retain for latest release.

59
votes

had a similar problem. In my case a viewController needed to get navigationController events, so it was registering as the navigation controller delegate:

 self.navigationController.delegate = self;

The crash occurs when that controller was dealloc'ed but was still the delegate for the view controller. Adding this code in dealloc had no effect:

-(void) dealloc
{
    if (self.navigationController.delegate == self)
    {
        self.navigationController.delegate = nil;
    }

because at the point that dealloc is called, the view controller has already been removed from the view hierarchy, so self.navigationController is nil, so the comparison is guaranteed to fail! :-(

The solution was to add this code to detect the VC leaving the view hierarchy just before it actually does so. It uses a method introduced in iOS 5 to determine when the view is being pop'ed and not pushed

-(void) viewWillDisappear:(BOOL) animated
{  
   [super viewWillDisappear:animated];
   if ([self isMovingFromParentViewController])
   {
      if (self.navigationController.delegate == self)
      {
           self.navigationController.delegate = nil;
      }
   }
}

No more crashes!

4
votes

For anyone who can't solve it, here are some other techniques:

https://stackoverflow.com/a/12264647/539149

https://stackoverflow.com/a/5698635/539149

https://stackoverflow.com/a/9359792/539149

https://stackoverflow.com/a/15270549/539149

https://stackoverflow.com/a/12098735/539149

You can run Instruments in Xcode 5 by clicking the project popup->Edit Scheme...Profile ->Instrument and choose Allocations or Leaks, then profile your app, then stop Instruments, click the info button in Allocations and "Enable NSZombie Detection".

However, for the messages that come directly from the com.apple.main-thread, this probably won't reveal anything.

I banged my head on this for over two hours and the answer turned out to be an over-release, which I discovered by commenting out a copy of my project by brute force until I found the culprit:

[viewController release];
viewController = NULL;

The problem is that release doesn't set the variable to NULL.

That means that setting it to NULL calls release again, decrementing the refcount and freeing the memory immediately until later when the variables that reference viewController are finished with it.

So either enable ARC or make sure your project consistently uses release or NULL but not both. My preference is to use NULL because then there is no chance of referencing a zombie but it makes finding where objects are released more difficult.

4
votes

I had met the same problem in iOS yesterday. I have made IAP in App "About" subview, and I have added Transaction Observer in "About" viewDidLoad. When I purchase for the first time, no problem, but after I back to main window and enter about subview to purchase again, the problem "message sent to deallocated instance" happened, and the App crashed.

- (void)viewDidLoad
{
    [[SKPaymentQueue defaultQueue] addTransactionObserver:self];                                           object:nil];
}

After I remove Transaction Observer in dealloc, the problem is solved.

- (void)dealloc
{
    // Even though we are using ARC, we still need to manually stop observing any
    // NSNotificationCenter notifications.  Otherwise we could get "zombie" crashes when
    // NSNotificationCenter tries to notify us after our -dealloc finished.

    [[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
}
4
votes

I had a very similar issue and I figured out it was due to navigation controller delegates set.

The below solved my issue,

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];

    if (self.navigationController.delegate != self) {
        self.navigationController.delegate = self;
    }
}

-(void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];

    if (self.navigationController.delegate == self) {
        self.navigationController.delegate = nil;
    }
}
2
votes

Had the same problem in OS X.

To solve this not enough - (void)dealloc method as @SoftwareEvolved already said. But unfortunately - (void)viewWillDisappear is available only on version 10.10 and later.

I introduced custom method in my NSViewController subclass where set all the zombie-dangerous references to nil. In my case that was NSTableView properties (delegate and dataSource).

- (void)shutdown
{
  self.tableView.delegate = nil;
  self.tableView.dataSource = nil;
}

That's all. Each time I'm about to remove view from the superview need call this method.

2
votes

I had the same Problem.It was difficult to find which delegate cause issue, because it does not indicate any line or code statement So I have try some way, Maybe it becomes helpful to you.

  1. Open xib file and from file's owner, Select "show the connections inspector" right hand side menu. Delegates are listed, set them to nil which are suspected.
  2. (Same as my case)Property Object like Textfield can create issue, So set its delegates to nil.
-(void) viewWillDisappear:(BOOL) animated{

[super viewWillDisappear:animated];

if ([self isMovingFromParentViewController]){

self.countryTextField.delegate = nil;

self.stateTextField.delegate = nil;

}

}