Ios Swift:以MODALY方式呈现并解除导航控制器
我有一个非常常见的iOS应用程序场景: 应用程序的MainVC是一个UIDABBarController。我在AppDelegate.swift文件中将此VC设置为rootViewController:Ios Swift:以MODALY方式呈现并解除导航控制器,ios,swift,uinavigationcontroller,dismissviewcontroller,Ios,Swift,Uinavigationcontroller,Dismissviewcontroller,我有一个非常常见的iOS应用程序场景: 应用程序的MainVC是一个UIDABBarController。我在AppDelegate.swift文件中将此VC设置为rootViewController: func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
window = UIWindow()
window?.rootViewController = MainVC()
window?.makeKeyAndVisible()
}
当用户注销时,我会提供一个导航控制器,其中LandingVC作为导航堆栈的根视图控制器
let navController = UINavigationController(rootViewController: LandingVC)
self.present(navController, animated: true, completion: nil)
在登陆VC内部,点击Login按钮,然后将LoginVC推到堆栈顶部
navigationController?.pushViewController(LoginVC(), animated: true)
当用户成功登录时,我将从LoginVC内部关闭导航控制器
self.navigationController?.dismiss(animated: true, completion: nil)
基本上,我试图实现以下流程:
一切正常,但问题是LoginVC从未从内存中释放。因此,如果一个用户登录和注销4次(没有理由这么做,但仍然有机会),我将在内存中看到LoginVC4次,登陆vc 0次
我不明白为什么LoginVC没有被解除分配,而LandingVC却被解除分配
在我看来(并纠正我的错误),因为导航控制器是显示的,并且它包含两个VCs(LandingVC和LoginVC),当我在LoginVC中使用disclose()时,它应该关闭导航控制器,因此两个VCs都包含
- MainVC:演示VC
- 导航控制器:显示VC
self.navigationController?.popToRootViewController(animated: true)
任何帮助都将不胜感激
====================================
我的登录代码:
经过大量搜索,我想我找到了解决方案: 激发我灵感的是所有人对这个问题和这篇文章的评论: 我将从我的编码哲学开始:我喜欢保持我的代码分离和干净。因此,我总是尝试创建一个包含我想要的所有元素的UIView,然后将其“链接”到适当的视图控制器。但是当UIView有按钮,并且这些按钮需要完成操作时会发生什么呢?众所周知,视图中没有“逻辑”的空间:
class LoginView: UIView {
// connect to view controller
var loginAction: (() -> Void)?
var forgotPasswordAction: (() -> Void)?
// some code that initializes the view, creates the UI elements and constrains them as well
// let's see the button that will login the user if credentials are correct
let loginButton: UIButton = {
let button = UIButton(title: "Login", font: UIFont.FontBook.AvertaSemibold.of(size: 20), textColor: .white, cornerRadius: 5)
button.addTarget(self, action: #selector(handleLogin), for: .touchUpInside)
button.backgroundColor = UIColor(red: 0.80, green: 0.80, blue: 0.80, alpha: 0.6)
return button
}()
// button actions
@objc func handleLogin() {
loginAction?()
}
@objc func handleForgotPassword() {
forgotPasswordAction?()
}
}
因此,正如文章所说:
LoginVC强烈引用了LoginView,它强烈引用了刚刚创建了对self的强烈引用的loginAction和放弃密码操作的闭包
你可以很清楚地看到,我们有一个循环。这意味着,如果退出此视图控制器,则无法将其从内存中删除,因为它仍然被闭包引用
这可能就是为什么我的LoginVC从未从内存中释放的原因。[剧透警报:这就是原因!]
如问题所示,LoginVC负责执行所有按钮操作。我之前所做的是:
class LoginVC: UIViewController {
// reference LoginView
var loginView: LoginView!
override func viewDidLoad() {
super.viewDidLoad()
setupView()
}
fileprivate func setupView() {
let mainView = LoginView(frame: self.view.frame)
self.loginView = mainView
self.view.addSubview(loginView)
// link button actions from LoginView to functionality inside LoginVC
// THIS IS WHAT IS CAUSING THE RETAIN CYCLE <--------------------
self.loginView.loginAction = loginButtonClicked
self.loginView.forgotPasswordAction = forgotPasswordButtonClicked
// pin view
.....
}
// our methods for executing the actions
fileprivate func loginButtonClicked() { ... }
fileprivate func forgotPasswordButtonClicked() { ... }
}
在这个简单的代码更改之后(是的,我花了10天的时间才弄明白),一切都像以前一样运行,但是内存感谢我
有几件事要说:
希望我能在同样的问题上帮助别人 在MainVC中调用Denit怎么样?你是如何得出LoginVC泄漏的结论的?如果仍然有一个强有力的参考文件保存在你的
landing/login
vc中,就会发生这种情况。它可以是回调、委托,甚至是属性。只要确保你也把这些东西卖掉就行了。完成后将它们设置为nil,您应该能够看到Denit被调用。请不要直接打电话给我deinit@AleksandrMedvedev如果您在xCode的“调试导航器”中使用“查看内存图层次结构”,您可以看到它。您的LoginVC中可能有一些强引用。你可能想在这里发布你的VC代码。
class LoginVC: UIViewController {
// reference LoginView
var loginView: LoginView!
override func viewDidLoad() {
super.viewDidLoad()
setupView()
}
fileprivate func setupView() {
let mainView = LoginView(frame: self.view.frame)
self.loginView = mainView
self.view.addSubview(loginView)
// link button actions from LoginView to functionality inside LoginVC
// THIS IS WHAT IS CAUSING THE RETAIN CYCLE <--------------------
self.loginView.loginAction = loginButtonClicked
self.loginView.forgotPasswordAction = forgotPasswordButtonClicked
// pin view
.....
}
// our methods for executing the actions
fileprivate func loginButtonClicked() { ... }
fileprivate func forgotPasswordButtonClicked() { ... }
}
fileprivate func setupView() {
...
...
...
self.loginView.loginAction = { [unowned self] in
self.loginButtonClicked()
}
self.loginView.forgotPasswordAction = { [unowned self] in
self.forgotPasswordButtonClicked()
}
self.loginView.textInputChangedAction = { [unowned self] in
self.textInputChanged()
}
}