인프런 영문 브랜드 로고
인프런 영문 브랜드 로고

Inflearn Community Q&A

ycc3819's profile image
ycc3819

asked

[Lv.2] SwiftUI Intermediate - Completing an App with Core Data and SwiftData

Weak Self

init-deinit의 무한루핑을 벗어나는 방법이 궁금합니다.

Written on

·

57

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() 
... }

어떻게 해결할 수 있을까요?

 

iosswiftswiftuiiphone-app

Answer 2

1

jacobko님의 프로필 이미지
jacobko
Instructor

안녕하세요 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 화면 확인
} 
ycc3819님의 프로필 이미지
ycc3819
Questioner

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

새로 하나 배워갑니다. 감사합니다!

0

안녕하세요, 인프런 AI 인턴입니다. ycc3819님.

init-deinit의 무한 루핑 문제는 메모리 관리 및 상태 변경 흐름에서 발생할 수 있는 일반적인 문제 중 하나입니다. 주로 View와 ViewModel 간의 상태 변화가 반복적으로 일어나면서 발생하게 됩니다.

이 문제를 해결하기 위한 몇 가지 접근 방법을 제시해드리겠습니다.

  1. ObservableObject 및 특성 래퍼 사용:

    • SwiftUI에서 ObservableObject@Published를 사용할 때, 적절한 상태 관리를 해야 합니다. @State@StateObject를 정확히 사용하여 상태가 변경될 때마다 변경 사항을 반영하도록 해야 합니다.
  2. Reference Cycle 확인:

    • 만약 ViewModel이 뷰에 자신의 self를 참조하고 있는 경우, 이를 해결하기 위해 weak 또는 unowned를 사용해 보세요. 이는 메모리 누수를 방지하고, 맞물린 리소스들이 올바르게 해제되도록 도와줍니다.
  3. SwiftUI 라이프사이클 준수:

    • SwiftUI는 상태가 변화할 때마다 뷰를 다시 렌더링합니다. 이때, 초기화 및 해제를 반복하여 불필요하게 호출하지 않도록 주의해야 합니다.
    • 특히, View의 프로퍼티로 직접 객체를 생성하기보다는, @StateObject를 사용하여 뷰 밖에서 상태를 관리하는 것을 추천드립니다.

다른 관련된 질문에 대한 게시물을 참조해 보세요. 특히 SwiftUI에서 states와 lifecycle을 적절하게 다루는 방법에 대한 내용은 많은 개발자들이 공유하고 있는 주제입니다.

유사한 사례를 다룬 두 가지 관련된 기존 Q&A를 추천드립니다:
- 강의 Weak Self 코드 실행시 오류에 대해: 이 게시물에서 강사님의 코드 리뷰를 통해 문제 해결 방법을 제시하고 있습니다.
- 안녕하새요(ViewModel): ViewModel 및 상태 관리에 관한 기초 개념을 이해하는 데 유용한 글입니다.

저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.

ycc3819's profile image
ycc3819

asked

Ask a question