I have a view controller that is never released once its parent view controller is removed from the view hierarchy and released. Every instance of it within the memory graph looks the same in that it has a single reference to CFNotificationCenter. It appears that other, not relevant, view controllers of a different class all have this same reference but still get released. The view controller in question also doesn't have any NotificationCenter observers so this makes no sense to me.
I have attached an image of the memory graph with the true name of the view controller redacted. I am also sure that this is the full graph of the view controller, I have not have selected to inspect a single reference.
What is happening here? Why won't it be released?
You can see exactly what code is referencing your View Controller:
First, turn on Malloc Stack Logging for your scheme (open the scheme editor with Cmd-Shift-,)
Then, click the "malloc" block that is pointing to your View Controller, and mouseover to the right of Backtrace to find the "Expand" button to see the full stack trace:
However, the most likely culprit is not actually the notification retaining your View Controller, as the reference is not strong (otherwise it would be a bold arrow).
The Xcode memory graph debugger doesn't show you when an object is retaining itself. In my case it was a simple case of retaining self in a closure in my viewDidLoad.
I have this issue in my project too. And I found that if I assign the object to nil while calling CFNotificationCenterAddObserver and CFNotificationCenterRemoveObserver, I still can receive the notification - and without the retain issue when deinitializing the object, which I added as the listener previously.
Related
Some of my view controllers don't get deallocated after being popped from view. I've gotten rid of the other strong references so I'm left with this internal retain cycle held through a reference form _externalObjectsTableForViewLoading. It's a private UIViewController property so I'm unable to clear it myself. I don't know if iOS has an API to clear it or why it's not being cleared after popping the view controller.
I've tested with with my app running in Release mode both in iOS 11 and 12. Running the app in Instruments renders the same stairs pattern seen in Xcode with the view controllers being retained.
Any ideas? Thanks in advance!
In your problem, is one viewController accessing another viewController? Our problem is that there was a non weak reference to a callback in another viewController.
As mentioned in your and in other posts on this, _externalObjectsTableForViewLoading is a viewController private property, but a storyboard related property. This leads me to think that your code has strong references to another object that is a view controller through a callback or through a direct property reference to its instance.
I have two view and second view is pushed from first view. For testing, I go to second view from first view and then I go back to first view. After that, I send nsnotification and in my second view, it receive my notification.
1) May I know why I receive notification in second view after it is pop up? For IBOutlets, I declare weak property also.
2) If it is still in memory, for other data like nsdictionary, nsstring, shall I use strong or weak property? Will those also in memory?
3) If I don't want my second view in memory totally, how shall I do?
If your second view controller is not released when you "go back" to the first view, then either
You are not really "going back" - you are accidentally creating a new first view controller and pushing it, which is unlikely; or:
You have a retain cycle in your second view controller.
I'm betting that you do have a retain cycle. You should try to track this down. You mention notifications: it is very easy to create an accidental extra retain when setting up a view controller as a notification observer, so that's probably the cause.
In particular, see my book's discussion of this topic:
If you called addObserverForName:object:​queue:usingBlock:, you will leak (under ARC) unless take elaborate precautions (such as doing the weak-strong dance in the block, to avoid a strongly retained self).
I have a view controller that displays a button. When I click the button, the corresponding selector needs to be called. However, with ARC, the application crashed with an EXC_BAD_ACCESS message.
-(IBAction)reseauPushed:(id)sender{
self.reseauVC = [[ReseauVCIpad alloc]initWithNibName:#"ReseauVCIpad" bundle:nil];
[self.viewCenter addSubview:self.reseauVC.view];
}
with
#property (strong, nonatomic) ReseauVCIpad *reseauVC;
and the crash log :
-[ReseauVCIpad performSelector:withObject:withObject:]: message sent to deallocated instance
I use ARC.
The button action :
-(IBAction)helloPushed:(id)sender{
NSLog("hello);
}
This ReseauVCIpad view controller is obviously getting deallocated some how. Either you accidentally are setting self.reseauVC to nil somewhere, or, more likely, the parent view controller, itself, is somehow getting deallocated. Is there any chance you did this addSubview technique for any of the preceding view controllers? And if not, how did you instantiate the root view controller?
To diagnose where the problem is, I'd suggest you add dealloc methods to all of your various view controllers so that you can confirm if any are getting deallocated prematurely. (Either set breakpoints or put in NSLog statements.) I'd wager you're seeing the parent of ReseauVCIpad getting deallocated, which is, in turn, allowing ReseauVCIpad itself to be deallocated.
By the way, as others have pointed out, the typical answer to this problem is to make sure you're doing the appropriate containment calls (show below), or if this child view controller takes up the whole screen, you should just be pushing to it or modally presenting it. Clearly, you have a strong reference to ReseauVCIpad, so the lack of containment calls isn't the source of the problem with ReseauVCIpad itself (though I wonder if you are doing this addSubview trick without containment calls with one or more of ReseauVCIpad's parent view controllers).
But you still should be doing these containment calls (or do a proper modal/push transition), regardless, to ensure your view controller hierarchy stays in sync with your view hierarchy (see WWDC 2011 video Implementing UIViewController Containment for lengthy discussion of why this is important). The appropriate containment calls for adding a subview with its own controller is, at a minimum, as follows:
- (IBAction)reseauPushed:(id)sender
{
self.reseauVC = [[ReseauVCIpad alloc]initWithNibName:#"ReseauVCIpad" bundle:nil];
[self addChildViewController:self.reseauVC];
[self.viewCenter addSubview:self.reseauVC.view];
[self.reseauVC didMoveToParentViewController:self];
}
For a more detailed description, see the video I referenced above, or see the Creating Custom Container View Controllers section of the View Controller Programming Guide for iOS.
And when you want to remove it, you should do the appropriate containment calls there, too:
- (void)removeReseau
{
[self.reseauVC willMoveToParentViewController:nil];
[self.reseauVC.view removeFromSuperview];
[self.reseauVC removeFromParentViewController];
self.reseau = nil;
}
This generally solves this issue (where the child view controller was deallocated). It won't solve your reseauVC problem (because you already have strong reference), but (a) you should do this wherever you do addSubview with a view controller, regardless; and (b) I show you the pattern in case you're doing addSubview elsewhere without maintaining a strong reference.
Several things. You should not add one view controller's content view to another view controller unless you set up a parent/child view controller relationship. This was added in iOS 5, and expanded in iOS 6 (and probably further expanded in iOS 7...) Look at methods like addChildViewController:, removeFromParentViewController, isMovingToParentViewController, and didMoveToParentViewController:
The easiest way to set up a parent/child view controller relationship is to use storyboards (which requires iOS 5) and an embed segue. (Which I believe was added in iOS 6). That takes care of all the housekeeping for setting up parent/child view controller relationships for you.
Your second view controller is being saved to a strong property, so I'm not clear on why it's being deallocated. I'm also not clear where the call to performSelector:withObject:withObject: is coming from. What source line is crashing, and are you using performSelector:withObject:withObject: in your code anywhere?
I've been dealing with this (somewhat) random bug for a while and can't figure out the problem. The context: I'm creating an UISplitView iPad app that have a UINavigationController inside the Master view:
Main menu in red, submenu in green and main content in purple.
This UINavigationController does not fill the entire Master view because I need some space to have a vertical menu. When a user select a button on the vertical side menu, it sets something new to the UINavigationController to show a UITableView with options. What I'm doing on every menu selection is:
[self.subMenu setViewControllers:#[subMenuViewController] animated:YES];
What happens is that I don't need to keep the menu history, so what I do is I set a new root view controller to the subMenu every time.
The issue is when I start messing with the device orientation. It doesn't have a clear pattern, but sometimes, when rotating, my app crashes. Now when I run it with Instruments, this is what I get:
167 Zombie -1 00:32.101.527 UIKit -[UITableView _spacingForExtraSeparators]
And the interesting thing is that the bad access happens on the previous root view controller of the subMenu. So if I tap "Events" and then after that I tap "Podcasts", the bad access happens on trying to access "EventsViewController".
So I'm guessing something is not right on my way of replacing the root view controller of the subMenu UINavigationController, but I'm not sure what it is. Maybe I need to make sure the current root view controller is released before setting a new one?
Any help is much appreciated. :)
It's not uncommon to crash in system library code due to something you didn't set up quite right. This might be that your UIWindow, UIApplicationMain or its content view or your view controller instance was not retained or got released somehow.
That shouldrotate method won't help if your controller isn't still around.
This is to identify which object got released.
For particularly thorny problems, you could add release, retain, and dealloc methods (that log and call super) to a suspect class of yours and see what's releasing it. Log the -retaincount to keep track (I only use this for diagnostic purposes,)
Or you can try this, set a breakpoint on -[UIDevice setOrientation:] and step through your code in the debugger.
To make debugging easier, you can type call (void)instrumentObjcMessageSends(YES) right in the debugger console to begin logging objc_msgSends to /tmp/, then continue execution and it will trace all the messages that are sent right up until the crash.
So first, you should implement the methods willRotateToInterfaceOrientation, willAnimateToInterfaceOrientation and didRotateToInterfaceOrientation (check the actual signature of these methods) in your view controllers that contain a UITableView.
In each of these methods check the your table view dataSource and delegate. I think this crash is caused by a release of the delegate or table view's datasource.
Also you should check what table view delgate/datasource methods are called during rotation.
And the last thing, make sure you discard the old instances of subMenuViewController and they are properly removed from the parent view controller.
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.