我的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
答案 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中。