使用协调器模式时奇怪的保留周期

时间:2019-07-19 12:12:19

标签: ios swift instruments retain-cycle coordinator-pattern

我正在使用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

2 个答案:

答案 0 :(得分:1)

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

enter image description here

我是通过“调试内存图”找到的:

enter image description here

还有PhoneInputViewController仍然存在,您可以检查为什么使用相同的方法

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

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

它保证您的控制器在弹出/更换后始终会死。

在您的情况下,我将首先尝试制作childCoordinators中的Coordinator,以保持对控制器的弱引用。

答案 1 :(得分:0)

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