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.
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.
I am building an application in Xcode 6.2, for iOS 8.1. I have a UIViewController (LevelViewController) which contains a UICollectionView whose cells each represent a level in my game (each one has a label w/ a number). Once one of the cells is selected I perform a show segue to an SKScene which loads all the data for that particular level.I also have a "menu" button which performs a show segue back to the main menu.
Functionally this all works, however I am having serious memory problems after performing both segues. After peeking in instruments it appears that when I segue out of the LevelViewController that all of the UILabel that I added for each individual UICollectionViewCell remain in memory, along with everything else contained in the cells. There should only be 192 labels (for 192 levels) but after performing this segue several times they add up to around 1000 in instruments.
Obviously these are not being deallocated in memory, It's my understanding that swift should take care of that, so i'm not sure what the problem is. I should also note that the UICollectionView was added programmatically, and no IBOutlets are used.
So how exactly can I get rid of those labels, and really, the UICollectionView itself when I segue away from the LevelViewController. Im seriously confused about this and it's ruining my St. Patricks Day. So for the love of all things Irish please help a lad out :)
Note: methods I have tried
self.collectionView.removeFromSuperView()
self.collectionView = nil
self.collectionView.deleteItemsAtIndexPaths(path)
It's hard to tell without seeing the code you're using to perform these segues. But I'm guessing that the problem has to do with how you are segueing between the view controllers.
I also have a "menu" button which performs a show segue back to the main menu.
If I had to guess based on your language above, instead of popping the Level ViewController (I'm assuming it is embedded in a UINavigationController), you are trying to segue back to the main menu using performSegueWithIdentifier: This will actually create a new instance of the view controller and push it onto the navigation stack (and retain the existing instance of it leading to your memory woes).
If that's indeed your problem, the solution is pretty simple: When your menu button is tapped, you should be calling popViewControllerAnimated or dismissViewControllerAnimated:completion:.
A simple little thing worth doing while you get accustomed to iOS segueing is to add de-initializers to all your view controllers as follows so as to get ongoing debug messages that objects are being deallocated as expected:
deinit {
debugPrintln("Name_of_view_controlled deinitialized...")
}
Happy St. Paddy's Day!
I have 3 view controllers that I'm navigating between. When I open my app, I start at Controller1, which I can then use to navigate to Controller2 or Controller3. I can navigate to each of them fine individually, however, after I go to Controller3, return to Controller1, then try to navigate to Controller2, I get an EXC_BAD_ACCESS with code = 1. There is no exception or error message given at all, it just takes me to my AppDelegate file and gives me that error code.
I don't know what the issue is, but something that seems relevant is that I'm setting Controller3 as the navigation controller's delegate. I have a fourth navigation controller that also is set as a delegate as well, and causes the same behavior when I from 1->4->1->2, just like with 1->3->1->2. I have no issue going from 1->4->1->3 or 1->3->1->4, only when 2 is involved. I'm not sure if the issue is the delegates, and the fact that 2 isn't being set as one. Once again, I can navigate to it fine by itself, but not after navigating to one of the other 2 sub-view controllers.
If you set Controller3 or Controller4 as the navigation controller delegate then you need to clear it in the viewWillDisappear function of those classes otherwise you will end up with an invalid reference and that is what is causing your crash
I ran into this same kind of crash, so I will share what the issue was for me:
I had created a subclass of UIWebView.
In my storyboard, I dragged a UIWebView onto the canvas and changed it's class to be my subclass.
That was all working. The app had already passed through our QA team, was "accepted" by the business owner, and we were ready to push it to the app store.
Then I was told, "You can't use UIWebView. You must use WKWebView."
Fine, I changed my subclass to inherit from WKWebView, tweaked my internal class logic, and....splat. Trying to segue to that view controller would crash just as noted by the OP.
The problem was my storyboard: Because it was trying to instantiate my subclass, it was basically attempting to create a WKWebView which is NOT necessarily supported in Interface Builder (my friend says it might be ok in later versions, but I didn't verify).
TL;DR
The moral of the story is that if you have a subclassed object on your story board whose ancestor can't be dragged from the toolbox, then you will probably crash when you segue.
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.
I changed navigation in my application from using UITabBarController to u UINavigationController. I.e. former solution (1st version) was based only on the TabBarController - 4 ViewControllers (one simple TableView, one simple custom view and one MapView with many overlays). The second version is based only on the UINavigationController.
In case of TabBarController it was clear and simple, everything worked fine, especially MapView. I mean: the MapView was loaded once (with a significant number of overlays) and when I went to another view and back to the MapView the MapView was still there with its overlays already loaded and displayed (simple check: MapView`s viewDidLoad was called just once per app run, I had some debug messages there).
Now I changed navigation logic to the UINavigationController. Everything works fine for the first look - but: the viewDidLoad (for each view) is called everytime I navigate to the view. It is annoying especially in the case of the MapView - the loading of overlays is performed everytime, it takes some time and it causes app crash in some cases.
OK, my questions:
Is it some kind of "common" behavior of NavigationController?
Can I change this behavior so viewDidLoad will be called just once?
And more - How can I influence the "display sequence" of some view?
I understand the logic is probably more complicated but I appreciate any answer or hint ;)
Some related circumstances:
TabBar and Navigation controllers are not combined.
I use storyboards, segues are designed in the UIB, no manual calling like perfomSegue or prepareForSegue in my code. One button triggers segue to MapView.
I use push segues.
I also tried to use modal segues but without any change of that behavior.
any of viewDidUnload is never called during segues among the views.
No memory warning received.
No memory leaks measured both on simulator and iPhone 4.
I tried to build a very simple temporary project / app that is concerned just about the Nav. Controller and other views without ANY coding, just storyboard. It was the same behavior.
There was an issue that causes app crash when I fast and periodically tapped to navigation button and back button between one view and the MapView. In most cases the app crashed when I tapped the back button on the MapView before it was fully displayed (i.e. its overlays). It was fixed when I added a 1 sec. delay method call in the viewDidDisappeared in the MapView. It is not a fair fix, I know ;)
A UITabBarController and UINavigationController are based on fundamentally different paradigms.
The UITabBarController is intended for the UIViewController on each tab to exist independently of each other and for the user to choose which they want to view. viewDidLoad only gets called once for each UIViewController because it is intended that each tab still exists in memory even as the user switches to a different tab.
The UINavigationController is a stack of UIViewControllers where each is related to the one above and beneath itself. The top UIViewController in the stack is always the one that is visible to the user. When a UIViewController is pushed to the stack, its viewDidLoad gets called because it is being loaded into memory. When the top UIViewControllergets poped off the stack, it is unloaded from memory, and viewDidUnload gets called on the way out (viewDidUnload is deprecated in iOS6 and won't get called, but the controller will still get dumped from memory). This is why viewDidLoad gets called every time that the user pushes a particular UIViewController onto the UINavigationController stack.