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.
Related
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.
Basic question:
Is there a reliably way to trigger showing modal UIViewControllers at any point in the app's lifetime (including from different threads)?
My current approach is to call presentViewController on the showing ViewController (found through window.rootViewController + hierarchy traversing but that's unimportant). This generally works, but is sometimes ignored due to things like a navigation action/animation taking place.
E.g. a background thread signals for a popup to be shown, and presentViewController is called on a ViewController in the process of being dismissed.
I've tried a few work arounds such as repeating the signal if the ViewController isn't shown (which led to some instances of it being show multiple times), but it's ended up being a game of whackamole.
An ideal solution would also allow navigation to take place underneath the popup, but the primary issue right now is just reliability.
edit
To be clear, I’m a seasoned developer. The threading is being handled properly, the instance and type management is working. My problem is trying to manage all
the corner cases, not the basics of how to do it.
If you need mechanism for thread safe showing different VCs in multithread environment, you can make some object which is responsible for presenting/dismissing controllers. And make some queue on presenting/dismissing. So when your signal occurs, your operation on presenting will be next after dismissing current VC in queue
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.
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.
I have a UITabBar holding 3 UIViewControllers.
One being a map, the other - a table, and the third - my own custom controller.
I noticed lately that my app has some issues when it comes to operating on low memory, and it's due to me not being clear about what to do when didReceiveMemoryWarning occurs.
Usually, and when with sufficient memory, all functions great. I would alloc all my controllers on the applicationDidFinishLaunchingWithOptions and all controllers work great.
But what happens when the memory is a bit low is that (for some reason) only my table is misbehaving.
First, I can see that the app received a memory warning. and then only my table gets a viewDidUnload.
At first I wasn't sure why my table became *completely empty*, but then I realized that my delegation methods stopped working rendering my reloadData essentially pointless.
So, I can see now that the didReceiveMemoryWarning is cascading through all my viewControllers. But viewDidUnload is called only on my table.
I want to know what is going on?
How do I recover from a viewDidUnload? If my view is nil, who is responsible for bringing it back?
Why is it that only my table is getting the viewDidUnload? I was reading this link and that one, and some of apples documentation but found no explanation to why my view doesn't recover.
What should I be doing in this case?