我正在使用MVVM +协调器构建新的应用程序。具体来说,我使用的是https://github.com/daveneff/Coordinator上的协调器模式。
在顶层,我有一个AppCoordinator,它可以启动子协调器RegisterCoordinator。注册流程完成后,AppCoordinator会切换其导航器的根ViewController,并应从内存中释放注册流程中使用的Coordinator和ViewController。
final class AppCoordinator: CoordinatorNavigable {
var dependencies: AppDependencies
var childCoordinators: [Coordinator] = []
var rootViewController = UINavigationController()
var navigator: NavigatorType
init(window: UIWindow, dependencies: AppDependencies) {
self.dependencies = dependencies
navigator = Navigator(navigationController: rootViewController)
dependencies.userManager.delegate = self
window.rootViewController = rootViewController
window.makeKeyAndVisible()
}
func start() {
if dependencies.properties[.user] == nil {
// Logged out state
let vc = AuthFlowViewController.instantiate(storyboardName: Constants.Storyboards.authFlow)
vc.delegate = self
navigator.setRootViewController(vc, animated: false)
} else {
// Logged in
let vc = HomeViewController.instantiate(storyboardName: Constants.Storyboards.home)
vc.viewModel = HomeViewModel(dependencies: dependencies)
navigator.setRootViewController(vc, animated: false)
}
childCoordinators = []
}
}
extension AppCoordinator: UserManagerDelegate {
func authStateChanged() {
// User logged in or logged out; show the correct root view controller
start()
}
func userChanged() {}
}
extension AppCoordinator: AuthFlowViewControllerDelegate {
func login() {
dependencies.userManager.changeUser(newUser: User(id: 1, name: "Kevin"))
}
func startRegisterFlow() {
let registerCoordinator = RegisterCoordinator(dependencies: dependencies, navigator: navigator)
pushCoordinator(registerCoordinator, animated: true)
}
}
同时,RegisterCoordinator只需将多个视图控制器推入导航器的堆栈中即可:
class RegisterCoordinator: CoordinatorNavigable {
var dependencies: AppDependencies
var childCoordinators: [Coordinator] = []
var navigator: NavigatorType
let rootViewController = PhoneInputViewController.instantiate(storyboardName: Constants.Storyboards.authFlow)
init(dependencies: AppDependencies, navigator: NavigatorType) {
self.dependencies = dependencies
self.navigator = navigator
rootViewController.delegate = self
}
func start() {}
}
extension RegisterCoordinator: PhoneInputViewControllerDelegate {
func phoneInputDone() {
let vc = PhoneValidationViewController.instantiate(storyboardName: Constants.Storyboards.authFlow)
vc.delegate = self
navigator.push(vc, animated: true)
}
}
extension RegisterCoordinator: PhoneValidationViewControllerDelegate {
func phoneValidationDone() {
let vc = GenderSelectionViewController.instantiate(storyboardName: Constants.Storyboards.authFlow)
vc.viewModel = GenderSelectionViewModel(dependencies: dependencies)
navigator.push(vc, animated: true)
}
}
整个注册流程完成后,最后一页可以保存用户,这将触发AppCoordinator中的authStateChanged
方法,然后该方法将更改导航器的rootViewController。然后,这也应该清理其子协调器。
尽管遗憾的是,尽管已正确释放了流程中的其他视图控制器,但RegisterCoordinator及其rootViewController(PhoneInputViewController)仍保持活动状态。
我尝试在childCoordinators = []
方法中手动执行start
,以确保AppCoordinator没有对RegisterCoordinator的强引用,但这仍然无济于事。
我不知道保持强引用的是什么,导致保留周期/内存泄漏。我有一个极简版本的应用程序,除了显示问题的基本要点外,基本上所有内容都已删除,位于GitHub:https://github.com/kevinrenskers/coordinator-problem。
答案 0 :(得分:1)
首先,您要在Coordinator.self
第132行的块中捕获协调器:
我是通过“调试内存图”找到的:
还有PhoneInputViewController仍然存在,您可以检查为什么使用相同的方法
我无法完全理解您的协调器模式实现的工作原理,但是最好不要向控制器保留强大的引用。
我一直在使用一些实现,其中控制器仅由UINavigationController
的堆栈保存,而window
保留UINavigationController
。
它保证您的控制器在弹出/更换后始终会死。
在您的情况下,我将首先尝试制作childCoordinators
中的Coordinator
,以保持对控制器的弱引用。
答案 1 :(得分:0)
rkyr的回答将我推向了正确的方向,我找到了问题的根源,并将包含此修复程序的PR发送到了我正在使用的原始Coordinator库中。因此,请在那里进行单行修复:https://github.com/daveneff/Coordinator/pull/1。