클래스 안정성 추론
Compose의 smart recomposition은 Composable의 입력값이 변경되지 않고, 값이 안정적으로 간주될 때 생략되는 것을 의미한다. Compose Runtime은 필요에 따라 recomposition을 생략하기 위해 입력값을 안전하게 읽고 비교할 수 있다는 것을 의미하며 안정성의 궁극적인 목적은 런타임에 도움이 되는 데 있다.
안정적인 타입에 충족하는 조건을 아래와 같다.
- 두 인스턴스에 대한 equals 함수의 호출은 항상 동일한 두 인스턴스에 대해 동일한 결과를 반환한다. 이는 비교값을 일관적이라는 것을 의미하며, 란타임은 이 사실에 의존하는 가정을 가진다.
- 타입의 public 프로퍼티가 변경될 때마다 composition은 항상 변경 사항에 대한 알림을 받게 된다. 이 조건이 없으면 Composable의 입력값과 본문을 구체화 할 때 반영되는 최신 상태 사이에 불일치가 발생할 수 있다. 이런 경우를 사전에 방지하기 위해 recomposition이 수행되는데 smart recomposition은 이런 불안정한 값에 의존하지 않는다.
- 모든 Public 프로퍼티는 원시 타입이거나 안정적이라 간주되는 타입을 가진다
모든 원시 타입들은 String을 포함하여 모든 함수형((Int) → String)과 함께 기본적으로 안정적인 타입으로 간주되는데, 그 이유는 타입들이 정의상 불변으로 간주되기 때문이다. 따라서 불변 타입은 변하지 않으며 composition에 또다시 변화를 알릴 필요가 없다.
불변 타입이 아니라도 @Stable 어노테이션을 추가하여 안정적인 타입으로 취급할 수 있다. 한 가지 예로 MutableState가 있는데, 해당 값이 변경될 때마다 Compose는 변경 사항에 대한 알림을 전달받기 때문에 스마트 recomposition을 위해서는 MutableState를 사용하는 것이 좋다.
하지만 개발자가 직접 @Immutable이나 @Stable 어노테이션을 사용해서 안정적인 타입을 명시하는 것은 시간이 지남에 따라 위험해질 수 있고 코드를 유지 보수하기 어렵게 만들 수도 있다.
따라서 클래스의 안정성을 개발자가 판단하는게 아니라 Compose 스스로 판단하게끔 하는 것이 바람직하다.
라이브 리터럴 활성화
라이브 리터럴은 Compose 도구들이 미리 보기에서 변경사항을 리컴파일 없이도 실시간으로 반영할 수 있다.
Compose Compiler가 여기서 하는 일은 일련의 표현식들을 MutableState에서 값을 읽어 들이는 형태로 변환하는 것이다. 이를 통해 런타임은 프로젝트를 리컴파일 없이도 변경 사항을 즉시 감지하여 반영할 수 있다.
Compose 람다식 기억법
Composable 함수에 전달된 람다식의 실행을 최적하하는 방법을 런타임에 알려주기 위해 IR을 수행한다.
해당 작업은 두 가지 유형의 람다식에 수행된다.
- Composable이 아닌 람다식 : 컴파일러는 이 유형의 람다식을 remember 호출로 감싸 메모리에 저장하기 위한 IR을 생성한다.
예를 들면 Composable 함수에 전달하는 콜백들이 그러하다. 여기서 remember는 슬롯 테이블을 사용하여 해당 람다식을 저장하고 나중에 읽을 수 있도록 한다. - Composable 람다식 : 컴파일러는 Composable 람다식에 대해 IR을 생성하는데 이는 람다식을 감싸고 composition에 람다식을 저장하고 읽는 방법을 런타임에 알려주기 위한 정보를 추가하기 위해서이다. 이 작업은 remember를 실제로 사용하진 않지만, 최종적으로 remember을 사용하는 것과 동일한 목표를 가진다.
예를 들면 Compose UI 노드를 호출할 때 Compose UI 노드에 전달하는 Composable 람다식의 본문을 예로 들 수 있다.
Composable이 아닌 람다식
이 작업은 Composable 함수에 매개변수 형태로 전달된 람다식 호출을 최적화하여 재사용이 가능하도록 한다.
@Composable
fun Badge(name: String, onClick: () -> Unit) {
}
여기서 onClick은 Composable에 전달되는 표준 Kotlin 람다식이다.
만약 호출 지점에서 전달하는 람다가 특정 변수 값을 캡처한다면 Compose는 Runtime에게 이 사실을 기억하도록 전달한다.
람다식을 기억하도록 만든다라는 의미는 remember 함수로 람다식을 감싸는 것을 의미하며, 이는 IR 작업을 통해 이루어진다.
Runtime은 입력 매개 변수를 포함하여 캡처하고자 하는 값이 일치하면 새 람다식을 만드는 대신 이미 존재하는 람다식을 재사용한다.
이 최적화는 값을 캡처하는 람다식에만 적합하며, 그 외 경우에는 Kotlin의 기본 최적화로도 충분하다.
이렇게 람다식을 기억하는 행위는 람다식이 캡처한 값에 기반한다. IR이 수행될 때 Compiler는 기억된 표현식의 타입과 일치하는 반환 타입을 가진 remember 호출을 앞에 추가하고, 표현식 반환 타입과 일치하도록 remember <T>와 같은 형태로 호출에 제네릭 타입 인자를 추가한다.
또한 람다에 의해 포착된 모든 값들을 remember<T>(arg1, arg2 …)와 같은 형태로 조건 인수로 추가한다. 이 인수는 비교를 위해 사용될 수 있으며 마지막으로 remember <T>(arg1, arg2 …, expression)와 같은 표현식을 위한 람다식을 추가하여 후행 람다로서 동작할 수 있도록 한다.
캡처된 값을 조건 인자로 사용하면 해당 값이 표현식 결과를 기억하기 위한 키로 사용되며, 값이 달라질때 마다 invalidation이 발생하여 UI를 업데이트하게 된다.
'Android' 카테고리의 다른 글
[Android] Compose 컴파일러 - (7) (0) | 2024.11.21 |
---|---|
[Android] Compose 컴파일러 - (6) (0) | 2024.11.20 |
[Android] Compose 컴파일러 - (4) (0) | 2024.11.18 |
[Android] Compose 컴파일러 - (3) (0) | 2024.11.15 |
[Android] Compose 컴파일러 - (2) (0) | 2024.11.14 |