3

我正在使用 MVVM + Coordinators 构建一个新应用程序。具体来说,我使用的是https://github.com/daveneff/Coordinator上的 Coordinator 模式。

在顶层,我有一个 AppCoordinator,可以启动子协调器 RegisterCoordinator。当注册流程完成后,AppCoordinator 会切换其导航器的根视图控制器,注册流程中使用的协调器和视图控制器应该从内存中释放。

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)
  }
}

当整个注册流程完成后,最后一个页面可以保存用户,这会触发authStateChangedAppCoordinator 中的方法,然后更改导航器的 rootViewController。这也应该清理它的子协调器。

遗憾的是,RegisterCoordinator 及其 rootViewController (PhoneInputViewController) 仍然保持活动状态——尽管流程中的其他视图控制器已正确释放。

我尝试childCoordinators = []start方法中手动执行以确保 AppCoordinator 没有对 RegisterCoordinator 的强引用,但即使这样也无济于事。

我不知道是什么保留了强引用,导致保留周期/内存泄漏。我有一个超级最小版本的应用程序,除了显示问题的基本要素外,基本上所有内容都被删除了,在 GitHub 上:https ://github.com/kevinrenskers/coordinator-problem 。

4

2 回答 2

3

首先,您在Coordinator.self第 132 行的一个块中捕获您的协调器:

在此处输入图像描述

我使用调试内存图发现了这一点:

在此处输入图像描述

还有 PhoneInputViewController 还活着,你可以检查为什么使用相同的方法

我无法完全理解您的协调器模式实现是如何工作的,但最好不要为您的控制器保留强引用。

我一直在使用一些实现,其中控制器仅由UINavigationController' 堆栈window保留,而UINavigationController.

它保证您的控制器在弹出/更换后总是会死掉。

在你的情况下,我会首先尝试让你childCoordinatorsCoordinator控制器保持弱引用。

于 2019-07-19T12:50:04.357 回答
0

来自 rkyr 的回答将我推向了正确的方向,我找到了问题的根源并将修复的 PR 发送到我正在使用的原始 Coordinator 库。因此,请查看那里的单行修复:https ://github.com/daveneff/Coordinator/pull/1 。

于 2019-07-19T13:43:12.600 回答