iOS

ARC란 무엇인가?(3)

스엠 2022. 5. 21. 22:07

주 내용은 다음에서 발췌하였습니다.

https://docs.swift.org/swift-book/LanguageGuide/AutomaticReferenceCounting.html

 

Automatic Reference Counting — The Swift Programming Language (Swift 5.6)

Automatic Reference Counting Swift uses Automatic Reference Counting (ARC) to track and manage your app’s memory usage. In most cases, this means that memory management “just works” in Swift, and you don’t need to think about memory management your

docs.swift.org

3번째 글에서는 어쩌면 가장 많이 retain cycle을 발생시키는 형태인 closure에 대한 것이다.

 

우선 간략하게 closure가 왜 retain cycle을 발생시킬 수 있는지에 대한 이유를 말하면 바로 

closure의 value capturing때문이다. 

value capturing은 말 그대로 값을 저장한다는 것이다.

그럼 바로 본론으로 들어가 보겠다. 

 

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("\(name) is being deinitialized")
    }

}


let heading = HTMLElement(name: "h1")
let defaultText = "some default text"
heading.asHTML = {
    return "<\(heading.name)>\(heading.text ?? defaultText)</\(heading.name)>"
}
print(heading.asHTML())

앞서 말했다시피 closure는 값을 저장!한다. 

따라서 위 코드를 보면 asHtml이라는 함수가 생성될때 self

즉 HTMLElement라는 자기 자신의 객체를 저장하게 된다.

 

그럼 위와 같이 코드가 선언 되었을때 참조 형태는 어떻게 될까?

 

위와 같이 두개의 클라스가 강한 순환 참조를 일으키는 것과 똑같은 상황이 발생한다. 

여기거 클라스간 강한 순환 참조와 다른 점은 closure안에서 몇번이든 self가 불려도 ARC는 오직 하나의 refrence count를 올린다는 것이다. 이 외에는 클라스간 강한 순환 참조와 같으므로 생략 하겠다. 

 

 

그럼 어떻게 약한 순환 참조로 만들까?

늘 똑같이 unowned, weak로 self를 선언 하면 된다.

lazy var asHTML: () -> String = {
        if let text = self.text {
            return "<\(self.name)>\(text)</\(self.name)>"
        } else {
            return "<\(self.name) />"
        }
    }
    
    
    -> 
    lazy var asHTML: () -> String = { [weak self] in // [unowned self] in 
    
        if let text = self.text {
            return "<\(self.name)>\(text)</\(self.name)>"
        } else {
            return "<\(self.name) />"
        }
    }

여기서 또 weak 와  unowned 의 차이점은 무엇일까?

일단 편의성이 차이가 있다. 

weak 를 쓸 경우 다음과 같이 써야만 한다.

    lazy var asHTML: () -> String = { [weak self] in 
    	guard let self = self else { return } //weak 는 옵셔널이기때문에 한다고 생각하면 편하다
        if let text = self.text {
            return "<\(self.name)>\(text)</\(self.name)>"
        } else {
            return "<\(self.name) />"
        }
    }
    
    아니면 
    
    lazy var asHTML: () -> String = { [weak self] in 
        if let text = self?.text {
            return "<\(self?.name)>\(text)</\(self?.name)>"
        } else {
            return "<\(self?.name) />"
        }
    }

확실한 nil checking을 해줘야하는 불편함이 있다. 

하지만 unowned 의 경우 다음과 같이 쓸 수 있다.

 lazy var asHTML: () -> String = { [unowned self] in 
        if let text = self.text {
            return "<\(self.name)>\(text)</\(self.name)>"
        } else {
            return "<\(self.name) />"
        }
    }

왜 그럴까? 

이전 글에서 말했다시피 unowned는 같은 생명주기 혹은 더 긴 생명주기를 가정으로 하기 때문이다. 

하지만 이것은 개발자의 역량으로 판단 하는 것이기 때문에 unowned로 해놨지만 실제 메모리에서 해제되는 경우가 생겨 앱이 크래쉬나는 상황이 발생할 수 있다.

따라서 weak로 선언한 경우 정확히 어느 시점에 closure가 해제 됬는지 체크가 가능하지만 unowned의 경우는 불가능 하다.

따라서 조금 귀찮더라도 weak를 쓰는 것이 좀더 바람직한 방법이라는 것을 알 수 있다. 

 

이것으로 ARC란 무엇인가에 대한 글을 마친다.

 

이전글 

https://sm-ios-story.tistory.com/3

 

ARC란 무엇인가?(2)

주 내용은 다음에서 발췌하였습니다. https://docs.swift.org/swift-book/LanguageGuide/AutomaticReferenceCounting.html Automatic Reference Counting — The Swift Programming Language (Swift 5.6) Automati..

sm-ios-story.tistory.com

 

'iOS' 카테고리의 다른 글

KeyChain vs UserDefaults(1)  (0) 2022.11.17
some 과 any 그리고 Any?(2)  (0) 2022.10.26
some 과 any 그리고 Any?(1)  (0) 2022.10.12
ARC란 무엇인가?(2)  (0) 2022.05.21
ARC란 무엇인가?(1)  (0) 2022.05.21