-
[SwiftUI] View의 Lifetime (feat. WWDC 2021)iOS 2024. 11. 1. 01:23
이 포스트는 WWDC2021: Demystify SwiftUI 세션 중 View lifetime에 대한 내용을 공부하며 옮겨적은 글입니다.
오역이나 잘못 이해한 부분이 있을 수 있으니 틀린 부분은 댓글로 알려주세요 :)View value 변화 시의 로직
- 동일한 View 정의에서 생성된 서로 다른 2개의 값(PurrDecibelView)이 있는 경우.
- SwiftUI는 비교를 수행하고 View가 변경되었는 지 알기 위해서 두 값의 copy를 유지할 것이다.
- 그 후 변경 전의 값은 파괴된다.
Key point
- View value는 일시적이고 View의 lifetime에 의존하지 않아야 한다.
- 하지만 View value의 identity를 제어할 수 있어야 한다.
View lifetime
- View가 생성되고 화면에 보여질 때 SwiftUI는 expicit identity와 structural identity를 조합하여 identity를 부여한다
- 업데이트로 인한 overtime 발생 시 View의 새로운 value들이 생성 된다
- 하지만 SwiftUI의 관점에서는 새로운 value들 또한 같은 View이다.
- View의 identity가 변경되거나 삭제될 때 View의 lifetime은 종료된다
- 어떤 View의 lifetime에 대해 말한다면, 그 View와 연관된 identity를 언급하고 있는 것이다
- View의 identity를 View의 lifetime과 연결지을 수 있다는 것은 SwiftUI가 상태를 지속할 수 있는 방법을 이해하는 기초이다.
@State / @StateObject
- SwiftUI가 어떤 View를 바라보면서 @State와 @StateObject를 확인하면 그 View의 lifetime 동안 해당 데이터를 유지해야한다는 것을 알고 있다.
- 즉 @State와 @StateObject는 View의 identity와 연관된 영구적인 저장공간이다.
- 어떤 View가 처음 생성되어서 identity가 시작될 때 SwiftUI는 @State와 @StateObject의 초기값들을 활용해 그들의 저장공간을 메모리에 할당할 것이다
- View의 lifetime 동안 SwiftUI는 @State나 @StateObject가 변경될 때 그 저장공간을 유지하고 View의 body는 재평가된다.
View value 변경 시의 흐름
- if문의 true 절과 false 절에 위치한 View는 서로 다른 identity를 가진 것으로 간주된다.
- 이것은 애니메이션에 영향을 미칠 뿐 아니라 상태의 지속성에도 큰 영향을 미친다.
- 처음으로 body를 평가하고 true 절로 진입하면 SwiftUI는 CatRecorder의 초기값과 함께 @State를 저장할 영구적인 저장공간을 할당한다.
- SwiftUI는 이 View의 lifetime동안 여러 종류의 action들로 변경이 발생하더라도 상태를 유지한다
- 만약 dayTime값이 변경되어 false 절로 진입하게 되면 SwiftUI는 CatRecorder.nightTimeStyle()이 별도의 identity를 가진 다른 View라는 것을 알고 있다.
- SwiftUI가 false View를 위한 새로운 저장공간을 생성하고 @State의 초기값과 함께 시작한 직후에 true View를 위한 저장공간은 메모리에서 해제된다.
- 이후에 다시 true절로 변하게 되면 다시 새로운 identity가 다른 새로운 View로 변한 것을 알고 새 저장공간을 생성하고 @State의 초기값과 함께 시작한 직후에 false View를 위한 저장공간을 해제한다.
- 언제든 identity가 변경되면 @State는 교체된다는 점이 중요하다.
Point
- @State의 지속성은 View의 lifetime을 따른다
- @State를 View의 본질과는 완전히 분리하고 View의 identity와 연결하기 때문에 매우 강력한 개념이다
- SwiftUI의 모든 것이 이 개념으로부터 파생된다
Data-driven components
- 데이터는 매우 중요하기 때문에 SwiftUI는 데이터의 identity를 View의 explicit identity로 사용할 수 있는 형태의 data-driven constructs들을 갖고 있다.
- ForEach가 그 대표적인 예시이다.
ForEach with 상수 range
- range의 범위를 상수로만 정의할 경우 SwiftUI는 @ViewBuilder로 생성된 View를 식별하기 위해 정의된 range내에서만 offset을 사용할 것이다.
- 상수 range를 요구함으로써 View의 lifetime동안 identity가 안정적임을 보장한다.
ForEach 잘못된 사용례 - dynamic range
- 상수 range가 들어가야 할 생성자에 dynamic range가 입력되면 에러를 발생시킨다.
ForEach with dynamic range
- Collection과 identifier역할을 할 프로퍼티에 접근하는 keypath를 받는 생성자를 사용한다.
- SwifUI가 Collection의 element들로 부터 생성된 모든 View들에 identity를 할당해야하기 때문에 identifier역할을 하는 프로퍼티는 반드시 hashable하여야 한다.
성능과 어플리케이션의 정확성을 위해 안정적인 identity를 선택하는 법
- 데이터에 안정적인 identity를 제공하는 것은 매우 중요하므로 표준 라이브러리는 이를 명확히 하기 위해 Identifiable 프로토콜을 정의하고 있다.
- SwiftUI는 Identifiable 프로토콜의 이점을 완전히 가져와 keypath의 생략을 허용하고 데이터와 View에 연관된 identity를 정의하기 위해서 프로토콜의 요구사항으로 제공된 identifier를 사용한다.
- Swift가 해결할 문제의 제약조건을 정확하게 설명하기 위해서 type 시스템을 활용하는 방법의 하나이다.
- ForEach의 생성자는 Data란 generic 매개변수가 가리키는 Collection과 Collection의 각 element로 부터 View를 생성할 방법이 필요하다.
- 이런 형태는 ForEach가 데이터 Collection과 View Collection 사이의 관계를 정의하고 있다는 직관을 주어야 한다.
- 가장 중요한 부분은 Collection의 element들에 Identifiable을 채택하고 있다는 점이다.
- Identifiable프로토콜은 데이터가 안정적인 identity 개념을 제공할 수 있도록 하고 그 덕분에 SwiftUI는 데이터의 lifetime동안 데이터를 추적할 수 있다.
Data Identity
- Identifiable Type과 @ViewBuilder를 가진 SwiftUI View들은 data-driven component들이다.
- 이러한 View들은 관련된 View들의 lifetime을 지정하기 위해 제공한 데이터의 identity를 사용한다.
- 좋은 identifier를 선택하는 것으로 View와 데이터의 lifetime을 제어할 수 있다.
Summary
- View value들은 일시적이고 View의 lifetime에 의존하지 않아야 한다.
- 하지만 View value들의 identity는 영구적이고View의 lifetime에 의존하며, 시간이 지남에 따라 연속성을 제공한다.
- View의 identity를 제어할 수 있고 상태의 lifetime을 명확히 지정하는데 사용할 수 있다.
- SwiftUI는 data-driven component들을 위해 Identifiable 프로토콜의 이점을 완전히 사용하므로 데이터를 위한 안정적인 identifier를 선택하는 것이 중요하다.
'iOS' 카테고리의 다른 글
[SwiftUI] Navigation (feat. WWDC2022) (0) 2024.11.06 [SwiftUI] UI Update (feat. WWDC2021) (0) 2024.11.03 [SwiftUI] View Identity (feat. WWDC 2021) (0) 2024.10.31 [RxSwift] UILabel이 subscribe를 사용할 수 없는 이유 (0) 2024.07.31 Alamofire를 구조화된 코드로 사용하기 (feat. URLRequsetConvertible) (2) 2024.06.19