티스토리 뷰

반응형

 

안녕하세요~ 개발자 멍구입니다. ☺️

개발 공부를 하다보면 항상 중요하게 다뤄지는 주제가 있지요. 바로 메모리 누수, 메모리릭(memory leak) 현상입니다. 서로를 강하게 붙잡고 있는 강한참조가 발생한 상태에서 관련 화면이나 객체가 사라졌는데도 메모리가 해제되지 않고, 남아있는 현상입니다.

ARC(automatic reference counting)으로 메모리를 관리하는 swift 언어 특성 상, 강한순환참조로 reference count가 0이 되지 않고 불필요한 메모리가 쌓이는 경우가 자주 생깁니다. 오늘은 이런 메모리릭 현상의 위험성과 주의사항, 자주 발생하는 케이스 몇가지를 소개해볼게요~ 

 


메모리릭 현상을 방치하면 생길 수 있는 위험성

메모리릭 문제를 고려하지 않고 개발을 하게 되면 어떻게 될까요? 자잘한 메모리릭 현상이 방치되는 경우, 앱에 큰 영향이 없는 것처럼 보일지도 몰라요. 하지만 이런 현상이 발생하는 화면이 많이 많이 쌓이게 되면, 그 앱을 유저가 사용하면 할 수록 불필요한 메모리가 쌓이게 될겁니다. 

더욱이 메모리릭이 발생한 화면에 큰 메모리를 잡는 영상이나 이미지, 데이터를 많이 사용하고 있다면, 불필요한 메모리의 누수는 급격하게 증가하게 됩니다. 이러한 메모리 누수 현상의 축적은 앱의 버벅임 등 성능 저하에 큰 영향을 미치고, 심지어 앱의 크래시를 유발할 수 있습니다. (경험담) 

따라서 안정성있는 앱이나 소프트웨어 개발 시 메모리릭 현상을 항상 염두하고 개발해야합니다. 개발하면서 메모리릭 현상이 발생하는 케이스 몇가지를 상기해볼까해요.

 


메모리릭 현상 발생 가능성이 높은 경우
1. non-escaping closure에서 참조할때 주의사항

func subscribeEvent(_ handler: @escaping () -> Void) {
    // ...
}

func handleEvent() {
    // ...
}

func doSomething() {
    subscribeEvent { [weak self] in
        // handleEvent 메서드를 weak하게 참조
        self?.handleEvent()
    }
}

함수가 종료된 후, 밖에서도 실행될 수 있는 closure 블럭을 escaping closure라고 합니다. 비동기 콜백 이벤트인 네트워킹이나 의존성작업을 할때 많이 활용하게 되는데요. escaping closure의 경우에는 함수 밖에서도 적절한 동작을 취하는 것을 보장하도록, 개발자가 적절하게 closure 내 객체들에 대한 참조순환 관리를 해주어야 합니다.

이 경우에 [weak self], [unowned self] 키워드를 붙여주어서 서로가 강한 참조를 갖지 않도록 하고, 메모리릭 현상을 방지할 수 있습니다.

 


2. delegate, dataSource 등의 대리자 사용 시 주의사항

final class SomeViewController: UIViewController {
    // var recognizerDelegate: UIGestureRecognizerDelegate?
    // 대리자 사용 시, weak 키워드 명시를 고려합니다.
    weak var recognizerDelegate: UIGestureRecognizerDelegate?

    // ...
}

개발을 하다보면, UIViewController 로 화면을 개발할때 복잡한 이벤트 처리나 타 화면 간의 데이터 공유 등에 delegate, dataSource를 사용하는 경우가 많은데요. 이 외에도 custom화 해놓은 delegate, dataSource 등의 대리자를 활용할 수 있습니다. 

이런 대리자 변수들은 weak 키워드를 붙여서 사용할지 고려하는게 좋습니다. 보통 다른 화면과 얽히는 구조로 사용되기때문에, weak 키워드를 붙이지 않으면 대리자 할당 후 동작과정에서 강한 순환 참조가 발생하기 매우 쉽습니다.

 


3. Rx, Combine 구독을 통한 구독정보 관리 시 주의사항

iOS개발을 할때 RxSwift/RxCocoa나 built-in framework인 Combine을 많이 사용하곤 합니다. 이 경우에 구독정보를 잘 관리해주어야합니다. 불필요한 구독정보가 쌓이는 경우, 해당 화면에서의 성능 저하 혹은 앱크래시를 유발 할 수도 있으며, 불필요한 구독정보의 참조 관계때문에 구독이 이루어지는 화면의 메모리가 불필요하게 쌓이는 문제까지 발생할 수 있습니다.

따라서, 구독이 이루어질때 꼭 필요할때에만 구독정보를 쌓고 있는지, 구독정보가 불필요해지는 경우, 정리하는 로직이 필요하지 않은지 고민하며 개발해야합니다.

위 코드를 보겠습니다. subscribeSomething 메서드의 구독은 단 한번만 필요한 상황이라고 가정합니다. 생애주기 간 단 한번만 호출되면 문제가 없지만,

화면 내/외부에서 해당 메서드가 의도치 않게 2번 이상 호출된다면, 불필요한 구독정보가 쌓이고, 의도치 않은 중복 이벤트 동작이나 메모리릭 문제가 발생할 수 있습니다.

 

 

만약 단 한번한 구독을 하고, 불필요한 구독정보를 쌓고 싶지 않다면, 구독 전에 구독정보를 한번 비워줄 수 있습니다.

 

 

만약 다양한 이벤트 구독과 얽히지 않고, 단독적으로 구독정보를 관리하고 싶다면, AnyCancellable? 타입의 cancellable 변수를 두어서 별도로 구독정보를 관리할 수도 있습니다. 

* AnyCancellable 타입은 생애주기가 끝나면 알아서 메모리가 해제되지만, 타이머 등에 사용되는 Cancellable의 경우, 구독이 불필요해질때 명시적으로 cancel() 호출을 고려해 주어야 합니다.

 


4. closure 내에 closure 자기자신의 상위 객체 참조할때 주의사항

viewController.closure = {
    // closure 내에 closure 상위 객체와 서로를 참조하는 경우
    viewController.dismiss(animated: true)
}

viewController.closure = { [weak viewController] in
    viewController?.dismiss(animated: true)
}

closure 내에 closure의 상위객체나 closure가 참조하는 객체를 사용하는 경우, closure는 해당 값을 강하게 capture하면서 메모리릭현상이 발생할 수 있습니다. 클로져 내에서 사용하는 인스턴스는 기본적으로 strong reference로 잡히고, 서로가 서로를 강하게 참조하는 상황이 발생하기 쉽습니다.

따라서 이런 경우에는 가급적 closure 내에 꼭 해당 객체를 사용해야하는 것인지 고민 후, 가급적 closure 내에서 사용하지 않거나, [weak self], [unowned self] 캡쳐리스트를 사용해서 reference counting을 하지 않도록 고려하여 구현할 수 있습니다.

 


이번에 개발경험을 토대로 메모리릭이 자주 발생하는 케이스를 공유하고, 메모리릭의 위험성과 그에 대한 주의사항을 몇가지 소개해봤습니다.

강한순환참조로 발생하는 메모리릭(memory leak) 현상을 간과하고 개발하면, 언젠가 매우 크리티컬한 앱 이슈가 발생할 수 있으니, 개발할때 상시 고려하고, 신경쓰는 습관을 가지면 좋습니다.

다음에는 메모리릭을 조기 발견하는 몇가지 방법도 소개해볼게요. 그 외 메모리릭이 발생하는 케이스가 떠오르면 추가로 소개해보도록 하겠습니다~!
관련 피드백이나 의견 항상 환영하고, 즐거운 개발 되시길 바랍니다. 감사합니다! 🚀

 

메모리릭(memory leak) 이슈 간단하게 해결하는 방법 ▼

 

iOS memory leak, 메모리릭 이슈 간단하게 확인하는 방법

앱 개발을 하다보면, 많은 트러플 슈팅을 경험하고, 이를 해결하게 되는데요. 크리티컬한 문제 중 하나가 앱 크래시 현상, 앱 강제종료 현상입니다. 🥹 앱 강제종료 현상은 여러가지 원인이 있

0urtrees.tistory.com

 

 

 

반응형
댓글
반응형
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/10   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
글 보관함