본문 바로가기
Swift

[Swift] weak 키워드와 Strong Reference Cycle

by Jimmy_iOS 2023. 8. 31.

delegate패턴과 메모리 누수

이번 글의 내용은 프로토콜을 사용할 때 weak 키워드를 제대로 사용하지 않아

메모리 누수가 발생하는 문제를 해결하는 방법에 대한 내용입니다.

델리게이트 패턴 사용

델리게이트 패턴을 사용하면 뷰컨트롤러에서 뷰에게 동작을 위임할 수 있습니다.

컬렉션뷰를 뷰컨트롤러가 아닌 뷰에 만들고 컨트롤도 위임할 수 있습니다.

이를 위해서는 delegate = self와 같이 델리게이트를 설정해줘야 합니다.

HomeView

final class HomeView: BaseView {
    // HomeViewController를 델리게이트로 위임
    var delegate: HomeViewProtocol?

    lazy var collectionView: UICollectionView = {...}()
		...
}
extension HomeView: UICollectionViewDelegate, UICollectionViewDataSource {
    ...
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        print(#function)
				// HomeViewProtocol를 통해  HomeViewController를 pop시킴
        delegate?.didSelectItemAt(indexPath)
    }
    
}

HomeViewController

protocol HomeViewProtocol {
    func didSelectItemAt(_ indexPath: IndexPath)
}

final class HomeViewController: BaseViewController {
    override func loadView() {
        let view = HomeView()
        view.delegate = self
        self.view = view
    }

    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    deinit {
        print(#function)
    }
}

extension HomeViewController: HomeViewProtocol {
    func didSelectItemAt(_ indexPath: IndexPath) {
        navigationController?.popViewController(animated: true)
    }
}

메모리 누수 문제

위 코드를 실행하게 되면 뷰컨트롤러가 메모리에서 해제되어야 할 시점에

deinit이 호출되지 않고 메모리 사용량이 계속 늘어나는 문제가 발생합니다.

HomeViewController에서 뒤로가기를 실행하고 다시 뷰컨트롤러에 진입하는

동작을 반복하면 메모리 사용량이 계속해서 증가하는 것을 확인할 수 있습니다.

HomeViewControllerHomeView가 서로를 강하게 가리키면서 강한 순환참조 사이클이 발생한 것입니다.

해결 방법

이 문제를 해결하기 위해서는 프로토콜에 AnyObject로 제약을 걸어

class에서만 프로토콜을 채택할 수 있도록 해야 합니다.

해결하기 위해서는 기존 코드에 weak 키워드를 추가해야 합니다.

HomeViewProtocol에 클래스 제약 조건이 없기 때문에 weak 키워드를 사용할 수 없다고 합니다.

컴파일러님 그게 무슨말인가요?

weak 키워드는 class의 RC카운트를 올려주지 않는 키워드입니다.

그러나 HomeViewProtocolclass, struct 모두 채택할 수 있습니다.

따라서 컴파일러가 에러를 보내는 것입니다.

그럼 어떻게 해결해야 할까요?

protocol HomeViewProtocol: AnyObject {
    func didSelectItemAt(_ indexPath: IndexPath)
}

프로토콜에 AnyObject 제약을 추가하여, 해당 프로토콜을 class에서만 채택할 수 있도록 합니다.

그러면 정상적으로 작동 하는지 확인해 볼까요?

deinit 메서드가 정상적으로 호출되어 메모리 누수가 발생하지 않습니다.

결론

델리게이트 패턴을 사용할 때 weak 키워드를 제대로 사용하지 않아

메모리 누수가 발생하는 문제를 해결하는 방법에 대해 알아봤습니다.

HomeViewProtocol에 클래스 제약 조건이 없기 때문에

weak 키워드를 사용할 수 없다는 컴파일러의 에러 메시지를 해결하기 위해서는,

프로토콜에 AnyObject 제약을 추가하여 해당 프로토콜을 class에서만 채택할 수 있도록 합니다.

이렇게 해서 메모리 누수 문제를 해결할 수 있었고,

deinit 메서드가 정상적으로 호출되어 메모리 누수가 발생하지 않았습니다.


Uploaded by N2T