본문 바로가기
iOS/Swift

강한 순환 참조 / 강한 참조 사이클 (Strong Reference Cycle)

by 헤콩 2020. 12. 20.
반응형

STRONG REFERENCE CYCLE

Strong Reference Cycle (강한 순환 참조)란 무엇을 말할까요? 저는 iOS 개발을 공부하면서 iOS와 관련된 게시글이나 정보를 찾다보면 메모리 관리에 관한 이야기가 정말 많다고 생각이 들었습니다. 그리고 이 게시물에서 다뤄볼 강한 순환 참조도 메모리와 관련되어 있습니다. 그래서 오늘은 강한 순환 참조에 대해서 아래 예시와 함께 살펴보겠습니다.

 

먼저, 아래 코드와 같이 PersonCar이라는 클래스가 있다고 가정합시다.

그리고 Person class는 Car 속성을, Car class는 Person 속성을 가지고 있습니다.

class Person {
    var name = "Jamie"
    var car: Car?
}

class Car {
    var model: String
    var lessee: Person?
    
    init(model: String) {
        self.model = model
    }
}

 

이 때 각각 인스턴스를 생성하고 나면, teacher과 rentedCar는 각각의 Person, Car 인스턴스에 강한 참조로 연결됩니다. 이 시점에서 참조카운트는 각각 1이라고 할 수 있습니다.

var teacher: Person? = Person()
var rentedCar: Car? = Car(model: "Benz")

 

그리고 아래코드를 수행하고 나면, teacher의 car 프로퍼티와 rentedCar 인스턴스가 강한 참조로 연결되고, rentedCar의 lessee 프로퍼티와 teacher 인스턴스가 강한 참조로 연결됩니다.

teacher?.car = rentedCar
rentedCar?.lessee = teacher

 

이렇게 되면, Person Instance와 Car Instance의 참조카운트는 2로 증가합니다.

 

그럼 teacher 변수와 rentedCar 변수에 nil을 할당하여 각각 인스턴스 소유권을 포기해봅시다.

teacher = nil
rentedCar = nil

 

하지만 teacher과 rentedCar이 소유권을 포기했어도 참조카운트가 1 이상이기 때문에 메모리에서 사라지지 않습니다. 두 인스턴스의 프로퍼티가 서로를 소유하고 있기 때문입니다. 그리고 nil로 인해서 두 인스턴스에 접근할 방법이 없기 때문에 메모리를 해지할 방법이 없습니다.

 

이렇게 강한 참조 때문에 인스턴스를 제대로 해지할 수 없는 경우Strong Reference Cycle(강한 순환 참조 또는 강한 참조 사이클)이라고 합니다. ARC는 메모리 관리를 자동으로 해주지만 강한 참조 사이클을 해결하지는 못합니다.

따라서 강한 참조 사이클약한(Weak) 참조, 비소유(Unowned) 참조를 사용해서 해결해야 합니다.

 


 

WEAK REFERENCE

강한 참조와 달리, 약한 참조(Weak Reference)인스턴스를 참조하지만 소유하지는 않으므로 참조 카운트가 증가하지 않습니다. 대상 인스턴스를 참조하고 있는 중에도 언제든 메모리에서 사라질 수 있습니다. 따라서 소유자에 비해 짧은 생명주기를 가진 인스턴스를 참조할 때 사용합니다.

 

 

그럼 예시를 통해 살펴보겠습니다.

약한 참조항상 옵셔널 형식으로 선언합니다. 그리고 var 키워드 앞에 weak 키워드를 선언합니다. 참조하고 있는 인스턴스가 해제되면 ARC는 자동으로 nil로 초기화합니다.

class Person {
    var name = "Jamie"
    weak var car: Car?
}

class Car {
    var model: String
    weak var lessee: Person?
    
    init(model: String) {
        self.model = model
    }
}
var teacher: Person? = Person()
var rentedCar: Car? = Car(model: "Benz")

teacher?.car = rentedCar
rentedCar?.lessee = teacher

 

이렇게 인스턴스의 car과 lessee를 약한 참조로 선언하게 되면, teacher과 rentedCar를 nil로 초기화했을 때 참조카운트가 0이 되어 메모리 누수 없이 코드를 짤 수 있습니다.

 


 

UNOWNED REFERENCE

비소유 참조(Unowned Reference)도 약한 참조와 같은 방식으로 프로퍼티를 참조합니다. 하지만 약한 참조와 다르게 옵셔널이 아닌 비옵셔널로 선언합니다.

그리고 ARC는 비소유 참조를 nil로 설정하지 않기 때문에, unowned 는 참조하는 객체의 메모리가 해제된 상태에서 접근하면 bad access 에러가 발생합니다.

 

비소유 참조는 비옵셔널로 선언해야할 때나, 인스턴스의 생명주기가 소유자와 같거나 더 긴 경우에 사용합니다.

class Person {
    var name = "Jamie"
    var car: Car?
}

class Car {
    var model: String
    unowned var lessee: Person
    
    init(model: String, lessee: Person) {
        self.model = model
        self.lessee = lessee
    }
}
var teacher: Person? = Person()
var rentedCar: Car? = Car(model: "Benz", lessee: teacher!)

teacher?.car = rentedCar

 

weak는 객체를 계속 추적하면서 객체가 사라지게 되면 nil로 바꾸지만, unowned는 객체가 사라지게 되면 댕글링 포인터가 남습니다. 이 댕글링 포인터를 참조하게 되면 crash가 나는데, 이 때문에 unowned는 사라지지 않을 거라고 보장되는 객체에만 설정되어야 합니다.

- 댕글링  포인터 : 원래 바라보던 객체가 해제되면서 할당되지 않는 공간을 바라보는 포인터

 


마무리

위에서 설명한 것처럼 강한 순환 참조서로가 서로를 소유하고 있어 절대 메모리 해제가 되지 않는다는 것을 말합니다. ARC가 편하게 메모리 관리를 해주지만 자칫 잘못하면 순환참조가 발생할 수 있어 주의해야 하는데요, 강한 순환 참조의 예시를 하나 더 들어보자면 delegate 패턴을 들 수 있습니다.

 

delegate를 하기 위해서는 일을 시키는 객체와 일을 하는 객체 두 개가 무조건 있어야 하는데, 아래 코드로 객체를 연결시켜줌으로써 FirstViewController와 SecondViewController는 서로를 소유하는 상황이 됩니다.

secondViewController.delegate = self

 

즉, FirstViewController에서 SecondViewController객체를 만듦으로써 SecondViewController를 소유하고, SecondViewController의 delegate를 FirstViewController로 연결해줌으로써 FirstViewController를 소유하게 되는 순환 참조 사이클이 발생하게 됩니다. (양방향으로 참조)

 

이를 해결하기 위해서는 SecondViewController의 delegate에 weak를 붙여주면 됩니다.

이렇게 되면 FirstViewController만 SecondViewController를 소유하기 때문에 순환 참조가 발생하지 않습니다.

weak var delegate: FirstViewProtocol?

 

 

 

 

 

Swift | 강한 참조와 약한 참조! 순환 참조 사이클 해결방법, 약한 참조(weak)와 비소유(unowned)참조

🦖 Strong Reference Cycle이란? class Person { var name = "Ahyeon" var car: Car? deinit { print("person deinit") } } class Car { var model: String var lesee: Person? init(model: String) { self.model..

ahyeonlog.tistory.com

 

반응형

댓글