[TIL] LifeCycle(App, ViewController)

rbw·2022년 6월 19일
0

TIL

목록 보기
27/97

오늘부터 UIKit을 공부해보려고 한다. UIkit 내용만 적는 공간을 따로 만드려고 함

App의 생명주기(Life Cycle)

App의 생명 주기는 App의 실행/종료 및 App이 Foreground(화면이 띄워져 있고, 사용중이라고 생각하면 편할듯)/Background(실행중에 전화가 오거나 할 때) 상태에 있을 때, 시스템이 발생 시키는 event에 의해 App의 상태가 전환되는 일련의 과정을 뜻함.

앱의 실행 과정을 살펴보겠습니다.

사용자가 앱 아이콘을 탭 하면, 스프링보드(SpringBoard)가 앱을 실행시켜 줍니다.

스프링보드는 아이폰의 홈 스크린을 관리하는 기본 앱입니다. 기타 작업으로는 WindowServer 스타트, 앱의 시작 및 부트스트랩, 시작 시 일부 장치의 설정이 있습니다.

스프링보드가 앱의 시작 화면에 애니메이션을 적용하는 동안, 앱과 필요한 공유 라이브러리가 메모리에 로드 됩니다. 결국 앱 실행이 시작되고, 앱의 대리자(application delegate)가 알림을 받습니다.

AppDelegate는 앱 딜리게이트 객체입니다. 이것은 UIResponder 클래스를 상속하고, UIApplicationDelegate 프로토콜을 구현합니다.

iOS의 주요 엔트리는 UIApplicationDelegate 입니다. 이것은 프로토콜이고, 유저 이벤트 종류인 앱의 실행, 백그라운드나 포그라운드에 진입, 앱의 종료, 푸쉬 버튼 등의 알림을 받으려면 이를 앱에 구현해야 합니다.

UIResponder 클래스는 AppDelegate를 유저 이벤트에 반응을 할 수 있게 만들어 줍니다. 그리고, UIApplicationDelegateAppDelegate가 앱의 라이프 사이클에 대한 응답과 관리를 하는 대리자 객체역할을 하게 해줍니다.

앱의 실행 상태

  1. Not Running State : 앱이 실행되지 않았거나, 시스템에 의해 종료됨
  2. Inactive State : 앱이 포그라운드에 진입하였으나, 이벤트를 받지 못함
  3. Active State : 앱이 포그라운드에 들어오고 이벤트를 처리 가능함.
  4. Background State : 이 상태에서,실행 가능한 코드가 있다면, 실행하고 그러한 코드가 없거나, 실행이 완료되었다면 앱은 즉시 일시 중지 됩니다.
  5. Suspended State : 앱은 백그라운드(in memory)에 있지만, 코드를 실행하지 않고, 시스템에 충분한 메모리가 없다면 시스템은 이 앱을 종료시킴.

Mapping app states with Code

// 먼저 이 메소드는 앱이 시작되거나 초기화 되면 호출 된다.
// 루트 뷰 컨트롤러는 이 단계에서 인스턴스화 된다.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?)
// 앱이 포그라운드에 들어가고, 활성화 되면, 아래 메서드를 호출합니다.
// 주로, 이 메소드에서 UI의 상태를 복원할 수 있습니다.
func applicationDidBecomeActive(_ application: UIApplication) {
    ...
}
// 이 메소드는 앱의 상태가 활성화에서 비활성화로 변경될 때 호출되고 알립니다.
// 주로 UI의 데이터를 저장할때 이 메소드에서 합니다
func applicationWillResignActive(_ application: UIApplication) {
    ...
}
// 이 메소드는 앱이 백그라운드에 들어가면 호출하고 알립니다.
// 이 메소드에서 우리는 유저 데이터를 저장하거나 파일 이나 데이터베이스 자원을 릴리즈 합니다.
func applicationDidEnterBackground(_ application: UIApplication) {
    ...
}
// 앱이 포그라운드에 들어가지만, 아직 활성화 상태가 아닐때 이 메소드가 호출됩니다. 
// 이 메소드에서 유저 데이터를 복원합니다.
func applicationWillEnterForeground(_ application: UIApplication) {
    ...
}
// 앱이 종료되면 호출되고 알립니다.
// 이 메소드에서 자원을 릴리스 하고, 또한 유저 데이터를 저장합니다.
// 약 5초 동안 작업을 수행하고 리턴합니다. 시간이 만료되기 전에 메서드가 반환되지 않으면 시스템이 프로세스를 완전히 중지할 수 있습니다
func applicationWillTerminate(_ application: UIApplication) {
    ...
}

앱의 실행부터 중지까지의 흐름

Main Run Loop란

이는 유저가 일으키는 이벤트를 처리하는 프로세스 입니다.

App Delegate는 시작시에 메인 런 루프를 설정하고, 이벤트를 처리하고, 뷰 기반 인터페이스에 대한 업데이트를 다루는 데 사용합니다.

이는 View와 관련되어 있으므로 Main 쓰레드에서 실행됩니다.

메인 스레드는 직렬 스레드이며 이는 사용자 관련 이벤트가 수신된 순서대로 직렬로 처리되도록 합니다.

이를 순서로 정리하면

  1. 유저가 이벤트를 발생시킴
  2. 시스템을 통해 이벤트가 생성됨
  3. UIKit 프레임워크를 통해 생성된 port로 해당 이벤트가 앱으로 전달된다.
  4. 이벤트는 내부적으로 Queue 형태로 정리되고 Event Queue에 쌓임
  5. Event Queue에 있는 이벤트들이 Main Run Loop에 하나씩 매핑 된다.
  6. UIApplication 객체는 이때 가장 먼저 이벤트를 객체로 어떤 것이 실행될지 결정한다.

라이프 사이클에 관한 질문들

앱이 백그라운드에서 포그라운드로 어떻게 재개되나요 ?

유저가 현재 백그라운드에 있는 앱을 실행하게 되면 먼저, 시스템은 앱을 비활성화 상태로 이동 시킨 다음 활성화 상태로 이동시킵니다.

앱이 재부팅 후 포그라운드에 들어가는 과정 ?

유저가 앱을 처음 실행하거나, 장치를 재부팅 한 후거나, 시스템이 앱을 종료시킨 후라면 시스템은 앱을 활성화 상태로 옮깁니다.

앱이 포그라운드에서 백그라운드로 가는 과정 ?

How can you opt out background execution ?

opt out의 뜻을 잘 모르겠지만, 백그라운드 상태를 해제한다 라는 느낌인듯 ?

UIApplicationExitsOnSuspend 키를 앱의 info.plist에 추가하고, 해당 값을 yes로 설정하여 백그라운드 실행을 명시적으로 고를 수 있다.

백그라운드 상태를 선택 해제 하면, 앱 라이프 사이클은 not running, inactive 상태와 active 상태의 사이가 되며, 백그라운드나, 중단 상태로 전환 되지 않습니다.

iOS 앱은 sms, 전화, 캘린더 등의 일시 중단(interrupts)에 어떻게 반응하나?

일시적으로 앱을 비활성화 상태로 옮긴 후, 해당 방해 행위(중단)를 승인할지 무시할지 유저의 결정이 있을때 까지 상태를 유지한다.

  • 인터럽트를 무시할 경우 앱은 재활성화 된다.
  • 인터럽트를 승인할 경우 앱은 중지 상태로 이동함

백그라운드 상태의 용도는 ?

  • 사용자가 떠난 위치에서 앱을 다시 실행하는데 도움이 되는 모든 앱 데이터를 저장할 기회를 제공
  • 필요하지 않은 앱 리소스 릴리스

앱의 백 그라운드 실행 시간을 어떻게 추가하는지 ?

  • 앱은 몇 초 동안 백그라운드에 있을 수 있으며 그 시간 내에 모든 코드를 실행할 수 있습니다.
  • 앱에 대한 추가 백그라운드 실행 시간을 요청하는 beginBackgroundTask(expirationHandler handler: (() -> Void)? = nil) 메서드를 호출할 수 있습니다.
  • 앱의 확장에서는 performExpiringActivity(withReason:using:) 메소드를 호출하여서 가능합니다.

이러한 객체들은 앱이 더 이상 포그라운드에 있지 않은 경우 더 많은 실행시간을 제공합니다. 이 API는 앱이 중단되기 전에 요청이 완료되도록 합니다.


ViewController Life Cycle

뷰 컨트롤러도 라이프 사이클이 존재한다. 코드를 통해서 알아보자

// 스크린에 보일 때 뷰의 요소들이 메모리에 로드가 됨을 의미함.
override func viewDidLoad() {
    super.viewDidLoad()
    print("view did Load")
}
// 화면이 이제 보여질때 함수
override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    print("view will appear")
}
// 화면이 보여지고 나서 호출되는 함수
override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    print("view did appear")
}
// 화면이 사라질것을 나타내는 함수
override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    print("view will disappear")
}
// 뷰가 사라지고 나서 호출되는 함수
override func viewDidDisappear(_ animated: Bool) {
    super.viewDidDisappear(animated)
    print("view did disappear")
}

// 출력 결과 
// view did load
// view will appear
// view did appear 
// view will disappear
// view did disappear

위의 코드에서 보다시피, viewDidLoad 가 제일 먼저 실행이 되고 그 다음 차례대로 실행이 된다.

ViewDidLoad

이는 뷰의 컨트롤러가 메모리에 로드되고 난 후에 호출이 됨을 의미한다. (Called after the controller view is loaded into memory)

이 메소드는 뷰의 로딩이 완료 되었을 때 시스템에 의해 자동으로 호출되기 때문에 일반적으로 리소스를 초기화 하거나, UI의 초기 구성을 위해 주로 사용합니다. 처음 한 번만 실행이 되므로, 해당하는 코드를 이 부분에 작성해주면 된다.

네비게이션 컨트롤러를 통해 다른 뷰를 갔다가 다시 돌아오면, viewDidLoad는 실행이 되지 않는다. 따라서, 이러한 경우에 특정 액션을 주고 싶다면, viewWillAppear 내부에 액션을 작성하면 된다.

ViewDidDisappear

이는 뷰가 사라지고 나면 호출이 되는데, 만약 네비게이션 컨트롤러에서 다른 뷰로 넘어간다고 하였을 때, 다른 뷰의 viewDidLoad, viewWillAppear가 먼저 일어난 후에 이전 뷰의 viewDidDisappear가 수행이 된다.

물론 다른 뷰가 viewDidLoad를 실행 한 적이 있다면, 그 다음에는 실행하지 않고, viewWillAppear만 실행이 된다.

위 2가지 정도는 특별하게 더 알아두면 좋을듯 하다.


참조

https://manasaprema04.medium.com/application-life-cycle-in-ios-f7365d8c1636

https://zeddios.tistory.com/43

profile
hi there 👋

0개의 댓글