UIViewController не деинициализирует (mvvm + координаторы)

0 Konrad Siemczyk [2018-04-30 12:06:00]

Из-за этой проблемы у меня болит голова. Почему мой SplashscreenViewController не деинициализирует? Вы, ребята, видите какие-либо потенциальные циклы удержания кода ниже? Я попытался проверить стек Malloc, но не нашел никакой полезной информации.

final class SplashscreenViewController: UIViewController {
    var viewModel: SplashscreenViewModelType!

    private let animationStartScale: CGFloat = 0.75
    private let animationEndScale: CGFloat = 1.0
    private let animationDuration: TimeInterval = 0.4

    private let splashscreenDuration: TimeInterval = 1.5

    @IBOutlet private weak var logoImageView: UIImageView!

    override func viewDidLoad() {
        super.viewDidLoad()

        animateLogo(scale: animationStartScale)
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        DispatchQueue.main.asyncAfter(deadline: .now() + splashscreenDuration, execute: { [weak self] in
            self?.viewModel.onFinish.onNext(())
        })
    }

    private func animateLogo(scale: CGFloat) {
        UIView.animate(withDuration: animationDuration, animations: {
            self.transformImage(scale: scale)
        }, completion: { _ in
            if scale == self.animationStartScale {
                self.animateLogo(scale: self.animationEndScale)
            } else {
                self.animateLogo(scale: self.animationStartScale)
            }
        })
    }

    private func transformImage(scale: CGFloat) {
         logoImageView.transform = CGAffineTransform(scaleX: scale, y: scale)
    }
}

Класс, который содержит экземпляр SplaschreenViewController, является его координатором. Код ниже:

final class SplashscreenCoordinator: BaseCoordinator {
    private let window: UIWindow

    init(window: UIWindow) {
        self.window = window
    }

    override func start() -> Observable<Void> {
        let vc: SplashscreenViewController = SwinjectStoryboard.instantiateInitialViewController()
        window.rootViewController = vc
        window.makeKeyAndVisible()

        let onFinish = vc.viewModel.onFinish
            .asObservable()
            .flatMap { [unowned self] _ -> Observable<Void> in
                guard self.window.rootViewController == vc else {
                    return Observable<Void>.empty()
                }
                if UserPreferencesManager.isLoggedIn() {
                    return self.startAppForLoggedInUser()
                } else {
                    return self.startAppForAnonymousUser()
                }
            }

        return onFinish
    }

    private func startAppForLoggedInUser() -> Observable<Void> {
        let tabBar = TabBarCoordinator(window: window)
        return coordinate(to: tabBar)
    }

    private func startAppForAnonymousUser() -> Observable<Void> {
        let startCoordinator = StartCoordinator(window: window)
        return coordinate(to: startCoordinator)
    }
}

И некоторый код из BaseCoordinator:

...
private func store(coordinator: BaseCoordinator) {
    childCoordinators[coordinator.identifier] = coordinator
}

private func free(coordinator: BaseCoordinator) {
    childCoordinators[coordinator.identifier] = nil
}

func coordinate(to coordinator: BaseCoordinator) -> Observable<Void> {
    store(coordinator: coordinator)
    return coordinator.start()
        .do(onNext: { [weak self] _ in
            self?.free(coordinator: coordinator)
        })
}
...

Заранее благодарю за ваши ответы!

swift mvvm rx-swift swinject


1 ответ


1 daltonclaybrook [2018-05-01 06:52:00]

Двумя способами я мог видеть, как SplashscreenViewController может быть предотвращено освобождение:

  1. Если window в котором находится rootViewController, не освобождается. Имейте в виду, что приложение сохраняет ключевое окно.
  2. В вашей функции start() закрытие Observable сохраняет переменную vc. Это Observable может быть сохранено в другом месте, либо явно, либо в DisposeBag.