[iOS] Coordinator 패턴 (self-deallocating)

Charlie·2022년 10월 4일
0

지난 글에서 deallocate가 되지 않게 하려고 parent coordinator가 child coordinators 프로퍼티를 가지게 하는 것을 알아보았다.
이 외에도 정석적인(?)방법으로 coordinator를 사용하면 여러가지 불편함이 있었다.

  • child를 계속 append 해주어야함
  • child coordinator의 플로우가 끝나면 parent에게 삭제해달라고 요청해야함
  • 버튼, 스와이프 등의 이벤트를 사용자로부터 받아야함

개념

기존에는 parent가 child coordinators를 가지면서 ARC에 의해 자동으로 deallocated 되는 것을 방지했다.
이렇게 하지 말고 view와 coordinator간의 강한 참조를 만들어서 coordinator가 retain되게 해보자.

MVVM 상에서 다이어그램을 나타내면 다음 그림과 같이 나타낼 수 있다.

  • Presenter (주로 navigation controller)는 view를 가진다.
  • view는 view controller를 가진다.
  • view controller는 view model에 대한 강한 참조를 가진다.
  • view model은 coordinator에 대한 강한 참조를 가진다.
    coordinator는 여러 화면을 나타낼 수 있으므로 coordinator는 여러 강한 참조를 가질 수 있다.
  • coordinator는 presenter (주로 navigation controller)에 대한 참조를 가진다. 이 때, 순환 참조를 방지하기 위해 약한 참조를 해야한다.

이렇게 구현을 한다면 view가 pop 되었을 때 view controller가 release되고, view model이 release되고 coordinator가 release된다.

coordinator의 마지막 참조가 끊긴 상황이라면 별도의 callback 코드 또는 다른 방법들을 취하지 않고도 (기존에는 child coordinators 프로퍼티에서 제거를 해주어야 했음) 자동으로 release시킬 수 있다.

구현

Coordinator

이제 childCoordinators와 같은 프로퍼티는 필요가 없어졌다.
그저 weak으로 선언한 presenter에 대한 참조만 있으면 된다.

class MainCoordinator: Coordinator {
	private weak var navigationController = UINavigationController?
    
    func start() {
    	...
    }
}

extension Coordinator: ViewModelDelegate { ... }

ViewModel

// Coordinator와 연결을 위한 protocol
protocol ViewModelDelegate { ... }

// ViewController와 연결을 위한 protocol
protocol ViewModelType { ... }

class ViewModel: ViewModelType {
	let delegate: ViewModelDelegate
}

ViewModel에서는 coordinator, view controller와 각각 소통할 수 있는 protocol을 만들어주면 된다.
중요한 점은, coordinator에 대한 참조가 strong이어야 한다는 것이다.

ViewController

class ViewController: UIViewController {
	let viewModel: ViewModelType
}

여기서도 마찬가지로 viewModel에 대한 참조가 strong이어야 한다.

결론

👍

이렇게 자동으로 deallocate가 가능하게 구현을 하면 coordinator를 release하기 위해서 코드를 작성하지 않아도 되고 따로 고려할 필요도 없어진다. 또한 iOS에서 화면을 dismiss 시키는 방법이 back button을 클릭하는 것 뿐만 아니라 스와이프 등의 다른 방법들도 있는데, 이렇게 dismiss시키는 방법에 구애받지 않을 수 있게 된다.

👎

view가 dismiss 되어도 제대로 release가 되지 않는다면 coordinator가 retain되면서 메모리릭이 발생한다.
하지만 이는 childCoordinators를 사용하는 방법에도 발생할 수 있는 문제이고, 이 방법에서는 오히려 메모리릭을 찾기 더 어렵다. 또한 수동적으로 release를 시켜줘야하는데, 코드를 작성하다보면 이를 잊어버리고 놓치는 경우도 있을 수 있다.

따라서 Instruments, VM Debugging을 통해서 정상적으로 release가 되어서 메모리 릭이 발생하는지 확인을 하는 것이 중요하다.

Reference

Self-deallocated Coordinator pattern in Swift

profile
Hello

0개의 댓글