22-4. 클로저에 대한 강한 참조 사이클 그리고 해결

🌈 devleeky16498·2022년 4월 23일
0

클로저에 대한 강한 참조 사이클

  1. 강한 참조 사이클은 클래스 인스턴스의 프로퍼티에 클로저를 할당하고 해당 클로저의 바디에 인스턴스를 캡처하는 경우에도 발생한다. 이 캡처는 self.someProperty처럼 인스턴스의 프로퍼티에 접근하거나 클로저는 self.someMethod()와 같이 인스턴스 메서드를 호출하기에 발생할 수 있다. 이는 모두 클로저가 self를 "캡쳐" 하므로 생기는 강한참조 사이클이다.
  2. 이 강한 참조 사이클은 클로저와 클래스 모두 참조타입이므로 발생한다. 어떠한 프로퍼티에 클로저를 할당하면 해당 클로저에 참조를 할당하는 것이다.
class HTMLElement {
	let name : String
    let text : String?
    
    lazy var asHTML : () -> String = {
    	if let text = self.text {
        	return "<\(self.name) > \(text) < \(self.name) >"
        else {
           	return "<\(self.name/>"
        }
    }
    
    init(name : String, text : String? = nil) {
    	self.name = name
        self.text = text
    }
    
    deinit {
    	print("this is deinited")
    }
}
//asHTML 프로퍼티는 요소가 실제 일부 타겟에 대한 문자열 값으로 렌더링되어야 할때만 필요하므로, 지연 프로퍼티로 선언된다. 
//이 지연프로퍼티는 초기화가 완료되고 self가 존재할 때까지 접근할 수 없기에 기본 클로저 내에서 self 참조가 가능하다.

var paragraph : HTMLElement? = HTMLElement(name : "p", text : "Hello world")
print(paragraph!.asHTML())
//print "<p>Hello world</p>"

//다음의 예시는 HTMLElement 인스턴스와 기본 asHTML값으로 사용되는 클로저 간 강한 참조 사이클을 생성한다.

//인스턴스의 asHTML 프로퍼티는 클로저에 대한 강한 참조를 유지한다.
paragraph = nil
//클로저는 여러번 self를 참조하나 인스턴스에 대한 하나의 강한 참조만 생성한다.
//다음과 같이 인스턴스를 해제할 경우에도 해제자가 출력되지 않고 참조사이클이 유지된다.

클로저에 대한 약한 참조와 미소유 참조

  1. 클로저에 캡처한 인스턴스가 항상 서로를 참조하고 항상 같은 시간에 할당 해제될 때 클로저의 캡처를 미소유 참조로 정의한다.
  2. 반대로 캡처된 참조가 향후 nil이 되는 경우엔 약한 참조로 캡처를 정의한다.
  3. 캡처된 참조가 nil이 안되려면 약한 참조보다 미소유 참조로 캡처되어야 한다.
class HTMLElement {
	let name : String
    let text : String?
    
    lazy var asHTML : () -> String = {
    	[unowned self] in 
        if let text = self.text {
        	return "<\(self.name>\(text)</\(self.name)>"
        } else {
        	return "<\(self.name />"
        }
    }
    
    init(name : String, text : String? = nil) {
    	self.name = name
        self.text = text
    }
    deinit {
    	print("deinitialized")
    }
}

//다음은 클로저 바디내 클로저 캡처 목록을 미소유 참조로 선언 한것을 제외 모두 동일하다.
//이 의미를 담기 위해 [unowned self]로 키워드를 명시한다.

var paragraph : HTMLElement? = HTMLElement(name : "P", text : "Hello world")
print(paragraph!.asHTML())
//print "<p>Hello world</p>"
//동일하게 출력된다. 그러나 이번엔 클로저에 의해 self의 캡처는 미소유 참조이고 캡처한 HTMLElement의 인스턴스를 강하게 유지하지 않는다.
paragraph = nil
//인스턴스를 해제하게 되면, 현재의 인스턴스는 완전하게 해제된다.
profile
Welcome to Growing iOS developer's Blog! Enjoy!🔥

0개의 댓글