[英]Weird retain cycle when using the Coordinator pattern
我正在使用MVVM +協調器構建新的應用程序。 具體來說,我使用的是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)
}
}
整個注冊流程完成后,最后一頁可以保存用戶,這將觸發authStateChanged
中的authStateChanged方法,然后該方法將更改導航器的rootViewController。 然后,這也應該清理其子協調器。
盡管可悲的是,盡管已正確釋放了流程中的其他視圖控制器,但RegisterCoordinator及其rootViewController(PhoneInputViewController)仍保持活動狀態。
我嘗試在start
方法中手動執行childCoordinators = []
,以確保AppCoordinator沒有對RegisterCoordinator的強引用,但即使這樣也無濟於事。
我不知道保持強引用的是什么,導致保留周期/內存泄漏。 我在GitHub上有一個超級極簡版的應用程序,基本上刪除了所有內容,除了顯示問題的基本要點外: https : //github.com/kevinrenskers/coordinator-problem 。
首先,您要在Coordinator.self
第132行的一個塊中捕獲協調器:
我使用調試內存圖發現了這一點:
還有PhoneInputViewController仍然存在,您可以檢查為什么使用相同的方法
我無法完全理解您的協調器模式實現的工作原理,但是最好不要保留對控制器的強大引用。
我一直在使用一些實現,其中控制器僅由UINavigationController
的堆棧保留,而window
保留UINavigationController
。
它可以確保您的控制器在彈出/更換后始終會死亡。
在你的情況,我會試圖使啟動childCoordinators
的Coordinator
,以保持弱裁判給你的控制器。
rkyr的回答將我推向了正確的方向,我找到了問題的根源,並將包含修復程序的PR發送到了我正在使用的原始Coordinator庫中。 因此,在那里進行單線修復: https : //github.com/daveneff/Coordinator/pull/1 。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.