ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Swift 기본]ARC 와 Weak vs Strong
    개발/Swift 2021. 10. 4. 19:50

    ARC - Automatic Reference Counting

    자동으로 메모리를 관리해주는 방식이다.

    특징은 참조 카운팅이 0 이될때만 메모리에서 해제한다.

     

    아래와 같이 프로퍼티 , 상수, 변수에

    클래스 인스턴스를 할당하는걸 강한 참조라고 함

    let sangsu = Person(name:"sangsu")
    var sangsu = Persion(name: "sangsu")
    
    let myFriend : Friend()
    myFriend.person = Person(name:"sangsu")

     

    아래의 경우 참조 카운팅을 한번 보자.

    let test1 = Person(name:"simon")
    let test2 = test1
    test1 = nil

    위의 test1 상수는 nil값이 할당되어 참조가 사라졌지만

    test2 상수는 여전히 Person(name:"simon) 을 바라보고 있을것이다.

    따라서 Person 인스턴스의 참조 횟수는 0 이 아닌 1 이다.

    즉 메모리에서 해지 되지 않는다.

    이런 특성 때문에 강한참조순환(retaion cycle)문제가 발생된다.

     

    var simon = Person(name:"simon")
    var samsungAPT = Apartment(name:"samsung")
    
    
    simon.apartment = samsungAPT
    samsungAPT.tenant = simon
    
    simon = nil
    samsungAPT = nil

     

    위와같이 각자의 프로퍼티에 클래스 인스턴스를 할당했을시

    nil을 할당해도 simon 과 samsungAPT는 없어지겠지만 서로 참조했던 

    연결은 끊어지지 않아 메모리 릭이 발생된다.

     

    위와 같은 상황을 방지하려면 두가지 방법이 있다.

    첫번쨰로 프로퍼티나 변수 앞에 weak을 붙임으로써 이런 현상을 방지할수있다.

    weak은 객체를 "소유" 하지않고 주소값만을 가지게 된다. 따라서 값을 지정해줘도

    참조카운팅이 늘지가 않는다. weak 프로퍼티를 사용할경우 항상 상수가 아닌 변수로 선언해야 한다.

    또한 옵셔널로 선언해야한다.

     

    unowned를 붙여서도 해결할수 있다.

    참조하는 인스턴스가 항상 메모리에 존재할것을 예상하여 (항상 값이 있음을 가정)

    이것 역시 참조 횟수를 늘리지 않는다. 

     

     

    위와같은 케이스는 클로져에서 self를 사용할때 빈번히 일어난다.

    //코드 출처 : https://minsone.github.io/mac/ios/swift-automatic-reference-counting-summary
    
    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 {
            println("\(name) is being deinitialized")
        }
        
    }

    위의 asHTML 클로져는 lazy를 사용했다.

    lazy는 init 이후에 실행되며 따라서 self를 사용할수 있다.

    그러나 self.text 를 사용해서 self를 강하게 참조 하게된다.

     

     

    var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
    paragraph = nil

    클로져가 해당 인스턴스의 name를 참조하기 떄문에 인스턴스에 nil을 할당하여도

    참조 카운트는 1이 되어 메모리 해제되지 않는다.

    같은 예로 viewDidLoad 에서 클로저안에 self.showAlert를 사용해 self를 참조했음을 볼수있다.

    해당 viewController 는 pop 되어도 deinit 되지 않는 문제를 발생 시킨다.

    이것 또한 [weak self] 를 넣어줘 해결할수 있다.

    댓글

Designed by Tistory.