[SwiftUI] Weak Self

Junyoung Park·2022년 8월 19일
1

SwiftUI

목록 보기
18/136
post-thumbnail

How to use weak self in Swift | Continued Learning #18

Weak Self

구현 목표

  • 클래스 참조 카운팅은 iOS의 메모리 관리 기법에 있어서 매우 중요
  • 강한 참조 사이클을 벗어나는 한 가지 방법

구현 태스크

  1. 클래스 참조 카운트를 숫자로 표현
  2. 클래스 참조 시 데이터 다운로드 등 비동기적 메소드를 처리할 때 메모리 누수 발생 가능
  3. 약한 참조를 통해 메모리 누수 방지

핵심 코드

func getData() {
        DispatchQueue.main.asyncAfter(deadline: .now() + 10, execute: { [weak self] in
            // LOGN DATA DOWNLOADING
            self?.data = "NEW DATA"
        })
    }
  • 비동기 처리를 담당하는 클로저 내부에 weak self를 선언, 현재 담당 selfnil이 될 수도 있기 때문에 이후 참조 카운팅을 할 때 자동으로 사라짐

소스 코드


import SwiftUI

struct WeakSelfBootCamp: View {
    @AppStorage("count") var count: Int?
    init() {
        count = 0
    }
    
    var body: some View {
        NavigationView {
            NavigationLink("Navigate", destination: WeakSelfSecondScreen())
                .navigationTitle("Screen 1")
        }
        .overlay(
            Text("\(count ?? 0)")
                .font(.largeTitle)
                .padding()
                .background(Color.green.cornerRadius(10))
            , alignment: .topTrailing
        )
    }
}

struct WeakSelfSecondScreen: View {
    @StateObject private var viewModel = WeakSelfSecondScreenViewModel()
    var body: some View {
        Text("Second View")
            .font(.largeTitle)
            .foregroundColor(.red)
        if let data = viewModel.data {
            Text(data)
        }
    }
}

class WeakSelfSecondScreenViewModel: ObservableObject {
    @Published var data: String? = nil
    
    init() {
        print("INIT")
        let curCount = UserDefaults.standard.integer(forKey: "count")
        UserDefaults.standard.set(curCount + 1, forKey: "count")
        getData()
    }
    
    func getData() {
        DispatchQueue.main.asyncAfter(deadline: .now() + 10, execute: { [weak self] in
            // LOGN DATA DOWNLOADING
            self?.data = "NEW DATA"
        })
    }
    
    deinit {
        print("DEINIT")
        let curCount = UserDefaults.standard.integer(forKey: "count")
        UserDefaults.standard.set(curCount - 1, forKey: "count")
    }
}
  • WeakSelfSecondScreen으로 네비게이션 이동할 때마다 WeakSelfSecondScreenViewModel을 초기화 생성, 데이터를 패치하는 이니셜라이저 구현
  • 네비게이션 이동 뒤 클래스 생성, 데이터 패치(getData) 메소드를 호출하기 때문에 어느 정도의 시간이 걸리지 알 수 없음
  • 백그라운드 스레드를 통해 네트워크/인터넷의 데이터를 다운로드
  • 뷰에서 벗어나는 등 현재 작업을 그만둘 때 이전의 데이터가 그대로 남아 있는 현상 발생 → 여러 번 뷰 방문 시 그 개수만큼의 클래스 생성, 메모리 낭비
  • 강한 참조를 약한 참조로 변경, 해당 selfnil이 될 경우 참조 카운팅을 하지 않겠다는 표시를 통해 해결

구현 화면

  1. 강한 참조

  2. 약한 참조

profile
JUST DO IT

0개의 댓글