Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/swift/18.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ios 使用自适应popover segue并将目的地包装在导航控制器中会导致内存泄漏_Ios_Swift_Popover_Uistoryboardsegue - Fatal编程技术网

Ios 使用自适应popover segue并将目的地包装在导航控制器中会导致内存泄漏

Ios 使用自适应popover segue并将目的地包装在导航控制器中会导致内存泄漏,ios,swift,popover,uistoryboardsegue,Ios,Swift,Popover,Uistoryboardsegue,假设我有一个视图控制器,单击按钮时使用自适应popover segue显示该控制器。现在,在某些情况下,我可能希望将目标视图控制器包装在(例如)导航控制器中。因此,我将自己设置为popoverPresentationController委托的委托,并实现presentationController:viewControllerForAdaptivePresentationStyle:方法 但我注意到了一些奇怪的事情:在某些情况下,对象没有被释放。如果在前面提到的方法中,我将显示的viewcon

假设我有一个视图控制器,单击按钮时使用自适应popover segue显示该控制器。现在,在某些情况下,我可能希望将目标视图控制器包装在(例如)导航控制器中。因此,我将自己设置为
popoverPresentationController
委托的委托,并实现
presentationController:viewControllerForAdaptivePresentationStyle:
方法


但我注意到了一些奇怪的事情:在某些情况下,对象没有被释放。如果在前面提到的方法中,我将显示的viewcontroller包装在导航控制器中:

func presentationController(controller: UIPresentationController, viewControllerForAdaptivePresentationStyle style: UIModalPresentationStyle) -> UIViewController? {
    return UINavigationController(rootViewController: controller.presentedViewController)
}
关闭时,导航控制器将取消分配,但显示的视图控制器仍保持分配状态

相反,如果我通过adaptive popover segue直接显示一个导航控制器,那么在关闭导航控制器时,它包含的导航控制器和详细信息控制器都会被正确释放


出于演示目的,请参考本测试项目(Swift):

动态包装导航控制器(点击“Popover,nav automatically added”(自动添加导航)按钮)时得到的结果:

如您所见,详细信息视图控制器从未解除分配


我查看了
presentationController:viewControllerForAdaptivePresentationStyle:
的文档,但没有具体提到所有权、强保留等。。。 我尝试将工具与分配工具结合使用,但在这个(简单的)案例中涉及了太多的保留/发布,我无法直接找到问题所在

有没有人遇到过这个问题?或者你对如何解决这个问题有什么想法


解决方案 正如@TomSwift在下面提到的,由于控制器和segue之间的循环引用,存在一个bug。解决这个问题的唯一方法是在segue(custom)的init方法中进行包装,并且仍然将目标控制器包装到导航控制器中

我已经在Github上更新了我的示例代码,以展示如何使用@Vasily提到的解决方案实现这一点,但仍然允许使用协议进行动态包装行为,而无需使用NSUserDefaults求助于黑客解决方案。

solution 您需要创建自定义UIStoryboardSegue类并重写init函数

样本:

class StoryboardSegue: UIStoryboardSegue {

override init(identifier: String?, source: UIViewController, destination: UIViewController) {
    super.init(identifier: identifier, source: source, destination: NavigationController(rootViewController: destination))
}
}
主故事板

结果


使用XCode8时,我注意到在
详细视图控制器
UIStoryboardSegue
之间有一个循环引用。我不认为有什么方法可以彻底打破这个循环,因为它是UIKit内部的。似乎有一个次级循环引用涉及到
NSDictionary
ivar“\u externalobjectstableforload”。你应该向苹果报告这件事

解决方案是不重用segue预加载的
DetailsViewController
。如果您自己手动实例化它,则可以绕过此问题。下面是一个可能的实现(需要您在情节提要中设置恢复标识符!):


我很感激你的努力,但这不是一个解决办法。事实上,它比我当前的代码占用更多的内存。以下是您的解决方案的日志输出:
——显示详细信息——加载的详细信息视图控制器(0x7fe75352eef0)加载的导航控制器(0x7fe75482e800)加载的详细信息视图控制器(0x7fe7536222c0)Deinit导航控制器(0x7fe75482e800)Deinit详细信息视图控制器(0x7fe7536222c0)
,如您所见,已分配2个详细信息视图控制器,但仅解除分配了1个。此外,在我将正确的重用和恢复标识符添加到详细信息场景之前,您的解决方案无法正常工作。我已编辑了答案。这个决定还不存在,但我在这个问题上留下了更多的想法。很有趣!我会看看你的建议,彻底测试一下,让你知道结果如何。添加了来自ViewController代码的wrap segues destinationViewController。我希望,这是一个解决方案)我添加了一些额外的初始化日志,这样你就可以看到分配了哪些对象,还可以记录地址以进行额外的验证。看看它是否在iOS 10上重现。如果是这样的话,一定要打开苹果的bug报告。我看到它在iOS10模拟器中复制。Xcode 8调试器显示了在内存中保存控制器的两个循环引用;两者都是UIKit内部的。我想向Apple提交一个bug。@djbe很高兴看到您发布的解决方案,但您根本没有使用
presentationController:viewControllerForAdaptivePresentationStyle:
。如果您希望显示的控制器通过该方法自适应地添加导航控制器包装器,您的解决方案将不适用。很好,不知道Xcode 8提供了这一点!通过快速搜索,这是新的“内存图”功能吗?@vasily首先提到了您建议的解决方案,不幸的是,这不是一个真正的解决方案,因为呈现的控制器分配了两次,并且只释放了一次。因此,内存泄漏仍然存在。
class StoryboardSegue: UIStoryboardSegue {

override init(identifier: String?, source: UIViewController, destination: UIViewController) {
    super.init(identifier: identifier, source: source, destination: NavigationController(rootViewController: destination))
}
}
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
}