관리 메뉴

김종권의 iOS 앱 개발 알아가기

[iOS - swift] 3. 클로저를 사용할 때 주의할 점 - nested closure 세 번째 본문

iOS 응용 (swift)

[iOS - swift] 3. 클로저를 사용할 때 주의할 점 - nested closure 세 번째

jake-kim 2024. 5. 29. 01:04

1. 클로저를 사용할 때 주의할 점 - Memory Leaks, Retain Cycle, nested closure 첫 번째

2. 클로저를 사용할 때 주의할 점 - nested closure 두 번째

3. 클로저를 사용할 때 주의할 점 - nested closure 세 번째

nested closure에서 weak로 잡지 않는 경우

  • closure가 두 번 생길 때 아래와 같은 경우가 존재
  • ss.instance?.f1 클로저 안에 ss.instance?.f2가 있고, 모두 RxSwift에서 제공하는 연산자 subscribe(with: self)로 접근함으로써 self는 weak로 잡은 상태
weak var instance: SomeClass?

button.rx.tap
    .subscribe(with: self) { ss, _ in
        ss.instance?.f1 {
            print("ss1")
            ss.instance?.f2 {
                print("ss2")
            }
        }
    }
    .disposed(by: disposeBag)
  • 이때 ss를 접근할 때 f1 클로저에서도 weak를 쓰지 않았는데 메모리릭이 발생할 것인지?
    • 정답) f1 클로저를 ss.instance에서 전역변수에 저장해놓지 않으면 메모리릭이 발생하지 않고, 저장해놓으면 메모리릭이 발생함

nested closure에서 메모리가 발생하는 상황

  • 2번째 nested closure에서 전역에 값을 저장하는 경우
weak var instance: SomeClass?

button.rx.tap
    .subscribe(with: self) { ss, _ in
        ss.instance?.f1 {
            print("ss1")
            ss.instance?.f2 {
                print("ss2")
            }
        }
    }
    .disposed(by: disposeBag)
    
class SomeClass {
    var closure1: (() -> ())?
    
    func f1(_ closure: @escaping () -> ()) {
        print("f1")
        closure1 = closure
        closure()
    }
    
    func f2(_ closure: @escaping () -> ()) {
        print("f2")
    }
}
  •  이유)
    • 클로저를 사용할 때 주의할 점 - nested closure 두 번째 글에서 알아 보았듯이 [weak self] 캡쳐리스트 뜻은 self가 가리키는 대상을 weak로 참조한다는 뜻이고 다시 내부 클로저에서 사용할 경우 weak self가 참조하는 대상을 strong하게 접근한다는 의미 
    • 즉, f1클로저 내부에서 ss가 가리키는 값을 strong으로 참조하고 있는데, 이 때 f1 내부에서 클로저를 전역에 저장해놓으면서 순환참조가 발생하는 것
ss --> f2
ss <-- f2
  • ss를 다시 weak로 접근해야 메모리릭이 나지 않음
button.rx.tap
    .subscribe(with: self) { ss, _ in
        ss.instance?.f1 { [weak ss] in // <-
            print("ss1")
            ss?.instance?.f2 {
                print("ss2")
            }
        }
    }
    .disposed(by: disposeBag)
  • 또는 f1클로저에서 클로저 내용을 전역에 저장하는 부분을 삭제해도 메모리릭이 나지 않음
class SomeClass {
    var closure1: (() -> ())?
    
    func f1(_ closure: @escaping () -> ()) {
        print("f1")
//        closure1 = closure <- 주석처리하면 메모리릭이 발생하지 않음
        closure()
    }
    
    func f2(_ closure: @escaping () -> ()) {
        print("f2")
    }
}

* 전체 코드: https://github.com/JK0369/ExMemoryLeakInNestedClosure

Comments