티스토리 뷰

반응형

# self in a clousre

 

다음과 같은 경우 Strong Reference Cycle (Retain Cycle) 이 발생한다. 

class SomeViewController: UIViewController {

    var someClosure: (() -> Void)?
    
    deinit {
        print("deinit")
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.someClosure = {
            print(self)
        }
    }
}

 

그래서 클로져 캡쳐리스트를 작성한다. 

class SomeViewController: UIViewController {

    var someClosure: (() -> Void)?
    
    deinit {
        print("deinit")
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.someClosure = { [weak self] in
            print(self)
        }
    }
}

 

 

# self in a closure in a closure

 

그렇다면 클로저 안의 클로저는 어떨까? 여기도 클로저 캡쳐리스트를 작성해줘야할까?

결론은 아래와 같다. 

 

Only declaring weak or unowned self in the capture list of the outer closure is enough to avoid retain cycles 
if you don't create a strong reference to self within the outer closure (e.g. by doing: guard let strongSelf = self 
else { return }).

 

1️⃣

outer closure에서 strong capture를 하지 않았기 때문에 inner closure는 안해줘도 된다. 

(the weakly captured self is still available in the inner closure)

 

deinit 이 호출된다. 

class SomeViewController: UIViewController {

    var someClosure: (() -> Void)?
    var innerClosure: (() -> Void)?
    
    deinit {
        print("deinit")
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.someClosure = { [weak self] in
            print(self)
            
            self?.innerClosure = {
                print(self)
            }
        }
        
        self.someClosure?()  // innerClosure 에 값이 들어가야하니까 호출함. 
    }
}

 

2️⃣

하지만 outer closure 내에서 strong capture를 하는 경우에는 inner closure 에도 클로져 캡쳐리스트를 작성해줘야한다. 

 

deinit 이 호출되지 않는다. 

class SomeViewController: UIViewController {

    var someClosure: (() -> Void)?
    var innerClosure: (() -> Void)?

    deinit {
        print("deinit")
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        self.someClosure = { [weak self] in
            ✅ guard let self else { return }
            print(self)

            self.innerClosure = {
                print(self)
            }
        }

        self.someClosure?()  // innerClosure 에 값이 들어가야하니까 호출함.
    }
}

 

 

inner closure 에도 클로져 캡쳐리스트를 작성해주면

deinit이 호출된다. 

class SomeViewController: UIViewController {

    var someClosure: (() -> Void)?
    var innerClosure: (() -> Void)?

    deinit {
        print("deinit")
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        self.someClosure = { [weak self] in
            guard let self else { return }
            print(self)

            self.innerClosure = { ✅ [weak self] in
                print(self)
            }
        }

        self.someClosure?() // innerClosure 에 값이 들어가야하니까 호출함.
    }
}

 

 

 

 



[ Reference ]

https://stackoverflow.com/questions/37837862/unowned-self-in-a-closure-in-a-closure

 

Unowned self in a closure in a closure

If I have a closure in another closure is it enough to use unowned/weak once in the outer closure to avoid retain cycles? Example: foo.aClosure({[unowned self] (allowed: Bool) in if a...

stackoverflow.com

 

 

 

반응형
댓글