인프런 커뮤니티 질문&답변
init-deinit의 무한루핑을 벗어나는 방법이 궁금합니다.
작성
·
126
1
ObservableObject를 활용하는 것보다,
새로운 Observable Macro를 활용하여 Migrating을 시도하려 했습니다.
주어진 WeakSelfInterViewModel 에 대해 @Observable을 적용하고, var data String? = nil 로 property를 만들었습니다. 하지만 Init과 Deinit이 반복되어 나타나는 무한루핑이 발생하였습니다.
class WeakSelfInterViewModel{
var data: String? = nil
... }
struct WeakSelfInter2: View {
@State var vm: WeakSelfInterViewModel = WeakSelfInterViewModel()
... }
어떻게 해결할 수 있을까요?
답변 2
1
안녕하세요 ycc3819 님.
저도 확인한 결과 @Observable 로 변경할 시 무한 루핑이 발생되는것을 확인하였습니다.
발생한 원인을 보자면..
1. 무한 루핑 발생 원인
• @State는 값 타입 상태를 관리하기 위해 설계되었습니다. 하지만 @Observable은 클래스 타입(참조 타입)을 기반으로 동작하며, SwiftUI가 상태 변경을 감지할 때 ViewModel이 반복적으로 초기화되는 문제가 발생합니다.
• 이로 인해 init과 deinit이 무한히 호출되며 루핑이 발생합니다.
2. 그래서 강의 코드 처럼 @StateObject를 사용해야 하는 이유
• 기존 강의 코드에서 @StateObject는 ObservableObject와 함께 사용되며, SwiftUI 뷰의 생명주기와 ViewModel의 초기화/해제를 안정적으로 관리합니다.
• @StateObject는 뷰가 다시 렌더링되더라도 ViewModel을 재사용하기 때문에 무한 루핑 문제가 발생하지 않습니다.
그래서 강의 내용 코드를 @Observable 로 바꿀때는 강의에서 다루지 않은 .onAppear, .onDisappear 를 사용해서 Weak self 약한 참조 내용을 확인 하실 수 있습니다. (@State, 와 init, deinit 을 사용하게 되면 라이프 사이클 충돌로 인해 무한 루핑이 되기 때문입니다. )
예시 코드
import SwiftUI
import Observation // @Observable 사용을 위해 임포트
// MARK: - VIEWMODEL
@Observable
class WeakSelfInterViewModel {
var data: String? = nil // 데이터를 관리하는 상태 변수
init() {
getData() // 초기 데이터 로드
}
func getData() {
// 비동기로 데이터를 로드하며, 강한 참조 순환을 방지하기 위해 [weak self] 사용
DispatchQueue.main.asyncAfter(deadline: .now() + 10) { [weak self] in
self?.data = "NEW DATA!" // 데이터 업데이트
}
}
}
// MARK: - SCREEN 1
struct WeakSelfInter: View {
var body: some View {
NavigationStack {
// 2번 페이지로 이동하며 ViewModel은 2번 페이지에서만 생성 및 관리
NavigationLink("2번째 페이지로 이동") {
WeakSelfInter2()
}
.navigationTitle("1번째 페이지") // 네비게이션 타이틀 설정
}
}
}
// MARK: - SCREEN 2
struct WeakSelfInter2: View {
@State private var vm = WeakSelfInterViewModel() // @State로 ViewModel 관리
var body: some View {
VStack {
Text("2번째 페이지")
.font(.largeTitle)
.foregroundColor(.red)
// ViewModel의 데이터가 있으면 표시
if let data = vm.data {
Text(data)
}
}
.onAppear {
// 뷰가 나타날 때 데이터 로드 확인
print("뷰가 나타남")
}
.onDisappear {
// 뷰가 사라질 때 로그 출력
print("뷰가 사라짐")
// ViewModel은 @State로 관리되므로 자동으로 메모리에서 해제됨
}
}
}
// MARK: - PREVIEW
#Preview {
WeakSelfInter() // 미리보기에서 WeakSelfInter 화면 확인
} 0
안녕하세요, 인프런 AI 인턴입니다. ycc3819님.
init-deinit의 무한 루핑 문제는 메모리 관리 및 상태 변경 흐름에서 발생할 수 있는 일반적인 문제 중 하나입니다. 주로 View와 ViewModel 간의 상태 변화가 반복적으로 일어나면서 발생하게 됩니다.
이 문제를 해결하기 위한 몇 가지 접근 방법을 제시해드리겠습니다.
ObservableObject 및 특성 래퍼 사용:
- SwiftUI에서
ObservableObject와@Published를 사용할 때, 적절한 상태 관리를 해야 합니다.@State및@StateObject를 정확히 사용하여 상태가 변경될 때마다 변경 사항을 반영하도록 해야 합니다.
- SwiftUI에서
Reference Cycle 확인:
- 만약 ViewModel이 뷰에 자신의
self를 참조하고 있는 경우, 이를 해결하기 위해weak또는unowned를 사용해 보세요. 이는 메모리 누수를 방지하고, 맞물린 리소스들이 올바르게 해제되도록 도와줍니다.
- 만약 ViewModel이 뷰에 자신의
SwiftUI 라이프사이클 준수:
- SwiftUI는 상태가 변화할 때마다 뷰를 다시 렌더링합니다. 이때, 초기화 및 해제를 반복하여 불필요하게 호출하지 않도록 주의해야 합니다.
- 특히, View의 프로퍼티로 직접 객체를 생성하기보다는,
@StateObject를 사용하여 뷰 밖에서 상태를 관리하는 것을 추천드립니다.
- SwiftUI는 상태가 변화할 때마다 뷰를 다시 렌더링합니다. 이때, 초기화 및 해제를 반복하여 불필요하게 호출하지 않도록 주의해야 합니다.
다른 관련된 질문에 대한 게시물을 참조해 보세요. 특히 SwiftUI에서 states와 lifecycle을 적절하게 다루는 방법에 대한 내용은 많은 개발자들이 공유하고 있는 주제입니다.
유사한 사례를 다룬 두 가지 관련된 기존 Q&A를 추천드립니다:
- 강의 Weak Self 코드 실행시 오류에 대해: 이 게시물에서 강사님의 코드 리뷰를 통해 문제 해결 방법을 제시하고 있습니다.
- 안녕하새요(ViewModel): ViewModel 및 상태 관리에 관한 기초 개념을 이해하는 데 유용한 글입니다.
저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.






단순히 @StateObject를 사용할때 ViewModel을 객체화 하여 View가 처음 생성, 초기화 될 때 활용한다는 사실만 알고 있었는데, View가 다시 렌더링 되더라도, 기존 ViewModel을 재사용하기에 가능하다는 말씀이신거군요!
새로 하나 배워갑니다. 감사합니다!