Swift의 참조 주기

Mar 14 2023
일부 숙련된 개발자도 이러한 실수를 범합니다.
개발자를 위한 메모리 관리는 일반적으로 사소한 일이 아닙니다. ARC(Automatic Reference Counting) 덕분에 iOS 개발자가 메모리를 쉽게 관리할 수 있습니다.
작성자의 다이어그램, 둘 이상의 개체 사이의 참조 순환

개발자를 위한 메모리 관리는 일반적으로 사소한 일이 아닙니다. ARC( Automatic Reference Counting ) 덕분에 iOS 개발자가 메모리를 쉽게 관리할 수 있습니다. 대부분의 시간 동안 작동합니다. 그러나 참조 주기는 매우 까다로우며 많은 숙련된 개발자가 그러한 실수를 하는 것을 보았습니다.

공식 문서는 주로 두 개체 A<->B 간의 기본 참조 순환에 대해 설명합니다. 이 글에서 보여드릴 것은 3~4개의 객체 사이의 참조 순환인데, 이는 더 알아보기 어렵습니다.

메모리 누수가 있는 앱

작성자의 간단한 데모용 앱

데모는 두 개의 화면이 있는 매우 간단한 iOS 프로젝트입니다. 첫 번째 화면인 "InitialViewController"에서 사용자는 버튼을 클릭하여 두 번째 화면인 "SampleViewController"를 시작한 다음 닫기 버튼을 클릭하여 두 번째 화면을 닫을 수 있습니다. 두 번째 화면은 VIP 클린 아키텍처를 사용하여 코딩됩니다.

사용자가 앱을 사용하는 동안 메모리가 누수됩니다. Xcode의 "디버그 메모리 그래프" 기능의 다음 화면에서 "SampleViewController", "SamplePresenter", "SampleInteractor" 및 "SampleRouter" 각각의 5개 인스턴스가 누출된 것을 볼 수 있습니다. 너무 많은 메모리 누수가 발생하면 앱이 시스템에 의해 종료되며 이는 앱 개발자에게 매우 당혹스러운 일이 될 것입니다.

Xcode, 디버그 메모리 그래프에서 작성자의 스크린 캡

여러 개체 내의 참조 순환

아래의 소스 코드를 자세히 살펴보고 메모리 누수가 발생하는 이유를 알아보겠습니다.

class SampleViewController: UIViewController, SampleViewControllerInterface {
    var interactor: SampleInteractorInterface?
    // omitted the rest for simplicity
}

class SampleInteractor: SampleInteractorInterface {
    var presenter: SamplePresenterInterface?
    // omitted the rest for simplicity
}

class SamplePresenter: SamplePresenterInterface {
    var viewController: SampleViewControllerInterface?
    var router: SampleRouterInterface?
    // omitted the rest for simplicity
}

class SampleRouter: SampleRouterInterface {
    var viewController: UIViewController?
    // omitted the rest for simplicity
}

작성자가 만든 다이어그램, 개체 간의 참조 관계

실제로 두 개의 참조 순환이 있습니다.

  1. Interactor->Presenter->ViewController->Interactor
  2. Interactor->Presenter->Router->ViewController->Interactor

이를 해결하기 위해 아래와 같이 약한 참조를 사용할 수 있습니다.

작성자의 스크린 캡, VIP에 대한 참조 주기 수정 방법

그러면 다음 다이어그램에 따라 더 이상 VIP에 대한 참조 순환이 없습니다.

작성자가 만든 다이어그램, 수정 후 개체 간의 참조 관계

블록 캡처 관련 참조 순환

후! 방금 두 개의 참조 순환을 수정했기 때문에 기분이 좋지만 아직 완료되지 않았습니다. 지금 앱을 실행하면 여전히 메모리 누수가 있음을 알 수 있습니다. 원인은 코드의 다음 부분입니다.

class CustomButton: UIView {
    var buttonClickHander: (() -> Void)?
    // omitted the rest for simplicity
}

class SampleViewController: UIViewController, SampleViewControllerInterface {
   private lazy var customButton: CustomButton = {
        let button = CustomButton()
        button.translatesAutoresizingMaskIntoConstraints = false
        return button
    }()

    private func setUpUI() {
        view.addSubview(customButton)
        customButton.buttonClickHander = {
            self.interactor?.customButtonClicked()
        }
        // ... 
    }
}

작성자가 만든 다이어그램, 객체와 블록 간의 참조 관계

문제가 있는 위치를 알게 되면 수정은 간단합니다. 아래를 참조하십시오.

작성자의 스크린 캡, 블록 캡처를 위한 참조 순환 수정 방법

요약

요약하면 여러 개체 내에서 발생하는 두 가지 유형의 참조 순환을 다루었습니다. 여기에서 데모 프로젝트를 찾을 수 있으며 수정 사항은 쉽게 검토할 수 있도록 풀 리퀘스트 에 준비되어 있습니다 . 이 기사가 참조 순환을 더 잘 이해하고 참조 순환을 피하는 데 도움이 되기를 바랍니다.