ViewControllers没有破坏

时间:2016-05-28 14:40:28

标签: ios swift memory hierarchy

我的iOS应用程序出现严重问题。

我的应用程序中有登录逻辑。登录然后注销时,某些视图控制器不会破坏。这会导致一些问题,例如,我使用NSNotifcationCenter发出的一些事件会被发出几次。这些问题是可以避免的,但我真的想要一个解决方案,以避免一些视图控制器在后台保持打开而不控制它。

控制登录逻辑的方式如下:

在app delegate start函数中,如果用户已经登录,我将根视图控制器设置为主可用视图控制器。因此,我没有做任何事情,根视图控制器通过故事板设置为登录视图控制器导航控制器。

当用户注销时,我使用模态segue将视图控制器转换回登录视图控制器导航控制器。

您可能已经了解我使用的是故事板,swift和最新的iOS。

我的注销代码是segue,它带我进入LoginViewControler:

self.performSegueWithIdentifier("Logout", sender: self)

我的应用委托代码:

if (userDefaults.valueForKey("uid") != nil) {
    let tabBarView = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("TabBarViewController") as! TabBarViewController
    let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
    appDelegate.window?.rootViewController = tabBarView        
 }

我做错了什么?

我很感激帮助:)

修改

我甚至尝试在注销操作中设置根视图控制器,但这也没有帮助。怎么可能呢?

这就是我现在注销的方式:

let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let newRootViewController = self.storyboard?.instantiateViewControllerWithIdentifier("LoginNavigationController") as! UINavigationController          

appDelegate.window!.rootViewController = newRootViewController

10 个答案:

答案 0 :(得分:2)

Adam H.是对的。如果这不起作用,那么检查具有强关系的IBOutlets和委托,并将它们更改为弱关系。即

@IBOutlet weak var collectionView: UICollectionView!

如果没有weak关键字,视频控制器将永远不会被处理掉。

根据您的项目设置方式,如果您每次有人注销时使用导航控制器(我推荐),您都可以使用

dispatch_async(dispatch_get_main_queue()) {
    self.navigationController.popToRootViewControllerAnimated(true)
}

这将弹出导航堆栈中的所有内容,它将处理所有视图控制器(除非你有很强的关系,否则他们不会被处置)

答案 1 :(得分:2)

无论您如何选择管理您的交易,每当视图控制器出现/消失时,不要忘记添加/删除观察者。

答案 2 :(得分:1)

如果已登录屏幕显示登录屏幕,并且登录屏幕显示已登录屏幕,则你将有一个循环,不断堆积在新的视图控制器上。要解决这个问题,一个人不能提出另一个,而是要放松它。另一种可能性是将每个实例保留为单例,并仅显示那些。

答案 3 :(得分:1)

我不久前实现了类似的东西,对我而言,似乎你正在滥用UINavigationController生命周期。 在阅读了两次问题之后,如果我理解正确,那么您似乎正在将登录视图控制器初始化为UINavigationController,它会堆叠视图控制器。用户注销后,您将保留堆栈,使用performSegue向堆栈添加更多ViewControllers。 您可以通过使用两个不同的场景来避免它 - 1)登录视图控制器自立。 2)你的应用程序的主流 - 可以从UITabController / UINavigationController开始,无论是两者还是其他。

在AppDelegate中,您检查 - 如果用户已登录 - 请执行逻辑并将应用程序rootVC设置为主流vc。 否则,将loginVC(UIViewController)设置为root。

这也允许您在需要时在主流中的任何位置弹出登录VC,而不会干扰主流。 在你的情况下,loginVC总是UINavigationController的root,所以你每次希望看到它或执行查看时都必须popToRootVC,这更糟糕,因为你创建了另一个UINavigationController实例,资源永远不会被释放。

显然,在编程中,在大多数情况下,一个问题有很多解决方案。我确定您的问题可以通过您的流程解决。我认为将loginVC堆叠在导航控制器上是一种糟糕的经历。

答案 4 :(得分:1)

部分问题是在rootViewController上设置新的UIWindow并不会从旧的根视图控制器中删除视图层次结构。这留下了各种各样的强引用,如果您使用Xcode的视图调试,您可以看到旧的视图层次结构仍然位于新的rootViewController视图后面层次结构。

这样的事情应该为你解决问题并让你的视图控制器退出:

let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let newRootViewController = self.storyboard?.instantiateViewControllerWithIdentifier("LoginNavigationController") as! UINavigationController          

appDelegate.window??.rootViewController?.view.removeFromSuperview()
appDelegate.window??.rootViewController?.dismissViewControllerAnimated(false, completion: nil)
appDelegate.window??.rootViewController = newRootViewController    

答案 5 :(得分:1)

在构建设置或批发中逐个文件删除ARC文件 每个项目(似乎你可以拥有非ARC项目,但是引用很弱 在它的时候:我认为不是运动,但你可以同时参加。)

然后在有问题的视图控制器中覆盖保留和释放 并通过打破覆盖来查看谁持有额外的参考 保留和释放。这应该是一种教育经验。

懒惰的方法是仅针对有问题的VC杀死ARC。

我很想知道这对于用swift编写的VC是如何工作的; - )

我认为这是住在objc住所一段时间的另一个原因 直到/如果swift编译器和运行时固化(如果有的话)。

希望这有助于任何人。

PS:在我的项目中编译一些swift文件需要永远,我没有 想法哪个swift文件导致了这个问题。咄。

答案 6 :(得分:1)

至于尚未表明,还没有很多信息可以正确地提供您问题的解决方案。 我建议你改变你的做法。我使用从AppDelegate启动的UINavigationController(navigationController)进行了类似的工作流程,如果我们登录,我将其作为ViewControllers:

(其中自我navigationController rootViewController 是另一个UINavigationController

self.setViewControllers[loginViewController, rootViewController] 

如果您没有登录,则只放入loginViewController:

self.setViewControllers[loginViewController] 

在这种情况下,您可以将rootViewController放在用户登录的位置。 这是我的2岁。

答案 7 :(得分:1)

我喜欢 root VC ,这只是空白。当应用程序启动时,根VC立即显示登录VC 作为根VC的子VC。当用户成功进行身份验证时,登录VC会通知根VC,后者然后将主VC 添加为根VC的子项,转换(带有动画效果很好)从登录VC到主VC(使用[self transitionFromViewController: toViewController: duration: options: animations: completion:]),然后删除登录VC作为孩子并丢弃它。注销时,主VC通知根VC,然后反向执行相同的操作。所以大多数时候你只有登录VC或主VC实例化;它们实例化的唯一时间是在过渡期间。

我发现segue对于构建快速原型非常有用,但对于生产应用程序,我不想使用它们。

答案 8 :(得分:1)

我猜看ViewControllers Stack你会这样: - 首次发布:LoginVC - 登录后:LoginVC - TabarVC - 单击Logout:LoginVC - TabarVC - LoginVC ....

所以你的下面的代码应该有效:

let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let newRootViewController = self.storyboard?.instantiateViewControllerWithIdentifier("LoginNavigationController") as! UINavigationController          

appDelegate.window!.rootViewController = newRootViewController

但它不是:(。在我看来,你应该总是让tabarVC rootViewController。并检查TabarVC,如果用户没有插入或用户按下注销,请提交loginVC并关闭它而不是performSegue。

答案 9 :(得分:0)

您必须删除以下内容:

self.performSegueWithIdentifier("Logout", sender: self)

而不是它,你可以覆盖这个 segue 方法,这就足够了:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        userDefaults.removeObjectForKey("uid") 
        if segue.identifier == "Logout" {
            let newRootViewController = segue.destinationViewController
            // newRootViewController is optional in case you want to pass vars
            // do whatever you want with your newRootViewController
        }
}

关于NSNotification,有两种方法可以删除它:

NSNotificationCenter.defaultCenter().removeObserver(self, name: "NotificationIdentifier", object: nil)
NSNotificationCenter.defaultCenter().removeObserver(self) // Remove from all notifications being observed

在Swift中,您可以将removeObservers放入新的deinit method中。

enter image description here