rxswift를 공부하고 disposablebag에 대해 공부하다가
순환참조에 대한 이해를 약간 이제서야 한 느낌이라
정리하려고 왔다
코드를 막 이것저것 갖다 써보다가
한발짝 떨어져서 다시 천천히 공부할때
비로소 개념이 이해가 되는 느낌이다
특히 이런 메모리 부분...
일단 DisposableBag에 대해 공부한게 있으니 간단히 정리
RxSwift에서 만들어진 구독(subscribe)은 계속해서 값을 주고받으며 관찰하는 역할을 한다.
이 구독이 뷰 컨트롤러나 객체와 엮여서 진행되는데, 만약 뷰 컨트롤러가 사라질 때까지 이 구독이 끊어지지 않으면 메모리에서 뷰 컨트롤러가 제대로 해제되지 않아서 메모리 누수(memory leak)가 생기게 된다.
여기서 DisposeBag이 등장하는데, DisposeBag은 구독을 모아서 관리하고, 뷰 컨트롤러가 사라질 때 자동으로 구독을 해제(dispose) 해 준다. 이 덕분에 구독을 일일이 신경 쓰지 않아도 메모리 관리를 안전하게 할 수 있다. DisposeBag이 없으면 모든 구독을 직접 해제해 줘야 하고, 깜빡하면 메모리 누수가 발생할 수 있는 상황이 벌어지게 된다.
let disposeBag = DisposeBag()
// Observable 구독 예제
observable
.subscribe(onNext: { value in
print(value)
})
.disposed(by: disposeBag) // disposeBag에 담아 뷰컨틀로러가 사라질 때 자동으로 구독 해제
disposebag에 대해 공부하다 메모리에 대한 이해가 점차 되기 시작했고,
애매하게 머리속에 있던 참조에 대해 공부
우선 프로그래밍에서 참조(reference)란 특정 객체나 메모리 위치를 가리키는 방식을 의미한다. 쉽게 말해, 참조는 어떤 객체가 어디에 있는지 가리키고 있는 “주소표” 같은 것이다.
예를 들어 변수 a가 특정 객체를 참조하고 있다고 하면, a는 실제 데이터를 복사해서 가지고 있는 게 아니라 그 객체의 위치를 알고 있어서 필요한 데이터를 그 위치에서 가져오거나 수정할 수 있는 것이다.
참조는 객체가 메모리에서 얼마나 오래 유지되는지에도 영향을 준다. 특정 객체가 다른 객체를 참조하고, 그 객체도 처음 객체를 참조하는 순환 참조(circular reference)가 발생하면, 서로를 붙잡고 있어서 메모리에서 해제되지 않는다. 이 경우 약한 참조(weak reference)를 사용해 두 객체 간의 강한 참조를 끊어 메모리 누수를 방지할 수 있다.
RxSwift의 예시를 들어보면, RxSwift 내에서는 클로저 내에 self를 참조하게 되는 경우가 많다. 그런데 self를 직접 참조하면 순환 참조가 발생할 수 있게 된다. 예를 들어, Observable이 self를 클로저 안에서 참조하고, self가 Observable을 DisposeBag에 담아 관리하게 되면 둘이 서로를 참조해서 해제되지 않는 상태가 되는 것이다.
이럴 때는 [weak self]를 사용해서 순환 참조를 끊어줘야 한다. 그렇지 않으면 뷰 컨트롤러가 메모리에서 해제되지 않아서 메모리 누수가 발생. 찾아보니 unowned self라는 것도 있어서 함께 정리.
self가 해제될 수도 있으니 Optional로 처리해주고, 필요하면 안전하게 self를 쓸 수 있게 한다.self가 절대 nil이 아닐 때 사용한다. self가 nil이 되면 크래시가 나기 때문에 확실히 nil이 될 수 없을 때만 사용하는 게 좋음.observable
.subscribe(onNext: { [weak self] value in
guard let self = self else { return }
self.updateUI(value)
})
.disposed(by: disposeBag)
그렇다면 약한 참조(weak)로 선언하면 정확히 어떻게 되는걸까?
약한 참조로 선언할 경우, 객체를 소유하지 않고 단순히 가리키기만 하는 상태가 된다. 약한 참조는 참조하는 객체가 메모리에서 해제될 때 이를 방해하지 않고, 참조 대상이 해제되면 자동으로 nil이 됨. 그래서 약한 참조는 항상 옵셔널 타입으로 선언된다. (weak var owner: Person?).
3번의 내용처럼 [weak self]를 사용해서 self와 Observable 사이의 강한 참조를 약하게 만들어 준 경우, self가 해제되면 self를 nil로 만들어서 순환 참조를 방지하게 되는 것이다.
처음에는 그냥 예제 코드에서 DisposeBag을 사용하고 [weak self]를 넣어주는 것만 따라 하다가, 나중에는 왜 이게 필요한지 뜯어보면서 이해하게 되면서 RxSwift의 메모리 관리가 훨씬 명확해 진 느낌이다. 앞으로도 이해가 안돼도 일단 공부하고 사용해 보면서 아~ 이런거구나 하고 깨닫게 될 일이 많을 것 같은데 아직은 퍽 즐거운 느낌이다. 보기만 해도 이해될 정도가 될 때까지 파이팅.