Let's say I have a view controller that I show using an adaptive popover segue when clicking on a button. Now in some cases, I might want to wrap the destination view controller in (for example) a navigation controller. So, I set myself as the delegate for the popoverPresentationController
's delegate, and implement the presentationController:viewControllerForAdaptivePresentationStyle:
method.
But I noticed something strange: in some cases, objects were not being deallocated. If, in the previously mentioned method, I wrap the presented viewcontroller in a navigation controller:
func presentationController(controller: UIPresentationController, viewControllerForAdaptivePresentationStyle style: UIModalPresentationStyle) -> UIViewController? {
return UINavigationController(rootViewController: controller.presentedViewController)
}
On dismiss the navigation controller gets deallocated, but the presented view controller remains allocated.
If, in contrast, I directly show a navigation controller via adaptive popover segue, then on dismiss both the navigation controller and the details controller it contains get deallocated correctly.
For demonstration purposes, please refer to this test project (Swift): https://github.com/djbe/AdaptivePopoverSegue-Test
What we get when dynamically wrapping in a navigation controller (tap the "Popover, nav automatically added" button):
--- Showing details ---
Loaded details view controller (0x7fab31632b70)
Loaded navigation controller (0x7fab32815600)
Deinit navigation controller (0x7fab32815600)
As you can see, the details view controller is never deallocated.
I checked the documentation for presentationController:viewControllerForAdaptivePresentationStyle:
but there are no specific mentions of ownership, strong retains, etc...
I tried using Instruments with the Allocations tool, but there are so many retain/releases involved in this (simple) case that I couldn't directly find the problem.
Has anyone ever encountered this issue? Or do you have an idea on how to solve this?
As mentioned below by @TomSwift, there is a bug due to a circular reference between the controller and the segue. The only way to solve this, and still wrap the destination controller in a navigation controller, is by doing the wrapping in the init method of the segue (custom).
I've updated my sample code on Github to showcase how this would be achieved using the solution as mentioned by @Vasily, but still allow for dynamic wrapping behaviour using protocols, without resorting to hacky workarounds using NSUserDefaults.
Using XCode8 I noted that there is a circular reference between the DetailsViewController
and the UIStoryboardSegue
. I don't see a way to cleanly break this cycle as it's internal to UIKit. There's seemingly a secondary circular reference involving an NSDictionary
ivar "_externalObjectsTableForLoading". You should report this to Apple!
A solution is to not reuse the DetailsViewController
that was pre-loaded by the segue. If you manually instantiate it yourself you can bypass this problem. Here's a possible implementation (requires you set the restoration identifier in the storyboard!):
func presentationController(controller: UIPresentationController, viewControllerForAdaptivePresentationStyle style: UIModalPresentationStyle) -> UIViewController? {
if (wrapInNavigationController) {
let vc = controller.presentedViewController
if let restorationIdentifier = vc.restorationIdentifier {
return NavigationController(rootViewController: vc.storyboard!.instantiateViewControllerWithIdentifier(restorationIdentifier))
}
}
return controller.presentedViewController
}
You need to create custom UIStoryboardSegue class and override init function.
Sample:
class StoryboardSegue: UIStoryboardSegue {
override init(identifier: String?, source: UIViewController, destination: UIViewController) {
super.init(identifier: identifier, source: source, destination: NavigationController(rootViewController: destination))
}
}
Main.storyboard
result
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With