이번 포스트에서는 뷰컨트롤러의 생명 주기에 대해서 알아보도록 하겠습니다. 앱을 구성하는 핵심 요소인 뷰컨트롤러 역시 자체적인 생명 주기를 가지고 있고, 이 과정을 잘 이해하는 것은 앱 개발에 있어 중요합니다.
이 포스트는 다음 가이드들을 참조하여 작성되었습니다.
start developing iOS Apps
UIViewController
View Controller Lifecycle
뷰 컨트롤러는 하나의 뷰를 전담해서 관리하는 객체로, MVC에서 ‘C’에 해당하는 객체입니다. 뷰컨트롤러는 자신이 가지는 뷰에 대해 의존성을 가지고, 뷰의 생성과, 자기가 관리하는 뷰가 뷰 계층에 추가되고 제거될 때의 필요한 추가 작업들을 전담해서 수행합니다.
-
init( ): 스토리보드나 nib(xib)파일을 통해 뷰컨트롤러를 초기화합니다. 이 과정은 뷰를 만들 때 사용할 정보를 뷰 컨트롤러에 저장하는 단계입니다.
-
loadView( ): 뷰를 실제로 생성해서 메모리에 로드합니다. 스토리보드나 nib(xib)을 사용하지 않는다면 이 메소드를 오버라이드해서 뷰를 만들고 뷰계층을 구성해야 합니다.
-
viewDidLoad( ): 뷰가 메모리에 로드된 직후에 해당 메소드를 호출합니다. loadView( ) 이후에 뷰나 뷰컨트롤러에 대한 추가적인 설정을 해줍니다.
-
viewWillAppear(_:): 뷰가 뷰계층에 추가되기 직전에, 또 해당 뷰가 나타나기 위한 애니메이션이 설정되기 전에 호출됩니다. 이 메소드를 오버라이드 해서 뷰가 화면에 나타나기 전에 필요한 추가적인 작업(ex. 다른 뷰를 조정하는 작업)을 수행할 수 있습니다. 이 메소드를 오버라이드 할 때는, 반드시 부모 클래스의 viewWillAppear를 호출하도록 정의해야 합니다.
-
viewDidAppear(_:): 뷰가 뷰 계층에 추가된 직후에 추가됩니다. 이 메소드를 오버라이드 해서, 나타나는 뷰에 대한 추가적인 설정을 해 줄 수 있습니다. 이 메소드를 오버라이드 할 때는, 반드시 부모 클래스의 viewDidAppear를 호출하도록 정의해야 합니다.
만약 어떤 뷰컨트롤러가 다른 뷰컨트롤러를 popover 방식으로 띄운다면, popover로 띄워진 뷰컨트롤러가 사라질 때 popover를 사용한 뷰컨트롤러에 대해서는 viewDidAppear가 호출되지 않습니다.
-
viewWillDisappear(_:): 뷰가 뷰계층에서 제거되기 직전, 이 뷰가 사라지기 위한 애니메이션이 설정되기 전에 호출됩니다. 이 메소드를 오버라이드 해서, 해당 뷰를 통해 일어난 변화를 저장하거나, 최초 반응자(first responder) 상태를 내려놓거나, 뷰가 나타났을 때 조정됐던 다른 뷰들을 원래대로 돌려놓는 등의 작업을 수행할 수 있습니다. 이 메소드를 오버라이드 할 때는 반드시 부모 클래스의 viewWillDisappear를 호출해야 합니다.
-
viewDidDisappear(_: ): 뷰가 뷰 계층에서 제거된 이후에 호출됩니다. 이 메소드를 오버라이드 해서, 뷰가 사라질 때 필요한 추가 작업을 수행할 수 있습니다. 이 메소드를 오버라이드 할 때는 반드시 부모 클래스의 viewDidDisappear를 호출해야 합니다.
보통은 will-Did가 쌍으로 호출되지만, 반드시 그런 것은 아닙니다. (위 도표에서 will 계열 메소드 사이에도 화살표가 있는 것을 확인할 수 있습니다) 따라서 어떤 작업을 제대로 수행하기 위해서는 will-did 쌍 뿐이 아니라, 상대쪽 will만이 호출되는 경우도 올바르게 대응해줘야 합니다.
시스템 메모리가 부족한 상황이 되면, 시스템은 뷰컨트롤에 메모리가 모자라다는 메시지를 보내는데, 이것이 didReceiveMemoryWarning() 메소드입니다. 이 메소드를 오버라이드 하여, 해제할 수 있는 메모리를 해제해여 최대한 메모리를 확보하는 작업을 수행합니다.
iOS 6 이전에는 메모리 부족 이벤트가 발생했을 때 해당 뷰컨트롤러의 뷰가 필요하지 않다고 판단되면 시스템이 이를 임의로 메모리에서 해제할 수 있었습니다. 이 때 사용되던 메소드들이 viewWillUnload/viewDidUnload입니다. 하지만 iOS6 이후로는 시스템이 이를 수행하지 않기 때문에, 해당 메소드들은 deprecated 되었습니다. 이전에 viewWillUnload/viewDidUnload에서 수행하던 작업은 didReceiveMemoryWarning()에서 대신 수행하면 됩니다. 또한 필요하다면 뷰를 메모리에서 해제하는 작업도 여기서 수행하면 됩니다. 뷰를 메모리에서 해제는 작업을 수행하기 전에, 반드시 해당 뷰가 뷰 계층에 있지 않은지 확인해야 합니다.
뷰 컨트롤러가 참조가 0이 되게 되면 deinit()을 통해서 뷰컨트롤러가 가지고 있던 뷰와 관련 자원들을 해제함으로써 완전히 생명주기가 끝나게 됩니다.