강한 참조
- 기본적으로 Swift에서는 참조하는 대상에 대해 강한 참조를 유지한다.
- 객체 간의 강한 참조가 형성되면 참조 대상은 메모리에서 해제되지 않는다.
- 강한 참조는 순환 참조(circular reference)를 유발할 수 있어 메모리 누수(memory leak)의 원인이 될 수 있다.
약한 참조
- 인스턴스를 참조시 Reference Count가 증가하지 않으며 참조하던 인스턴스가 메모리에서 해제된 경우 (nil이 된 경우) 자동으로 nill이 할당되어 메모리가 해제된다.
순환 참조란?
- 두 개의 객체가 서로가 서로를 참조하고 있는 형태!
예시)
class Person {
let name: String
var friend: Person?
init(name: String) {
self.name = name
}
}
var person1: Person? = Person(name: "John")
var person2: Person? = Person(name: "Sarah")
person1?.friend = person2
person2?.friend = person1
weak var weakPerson1 = person1
weak var weakPerson2 = person2
person1 = nil
person2 = nil
print(weakPerson1)
print(weakPerson2)
위의 예시에서 person1과 person2 간에 강한 참조가 형성되어 순환 참조가 발생한다. 이 경우, person1과 person2는 메모리에서 해제되지 않는다.
하지만, weakPerson1과 weakPerson2는 약한 참조로 선언되어 있으므로 person1과 person2가 nil로 설정된 후에는 자동으로 nil로 설정된다!
문제점
- 서로가 서로를 참조하고 있어서 Reference Count가 0이 되지 못한다.
- 해당 인스턴스를 가리키던 변수에 nil값을 넣는 다면 인스턴스에 접근할 수 있는 방법도 없어 메모리 해제가 불가능 함.
- 어플이 죽기 직전까진 memoy leak이 계속 발생하게 됨
- 이렇게 strong을 사용해서 순환참조에 문제가 생긴 경우를 강한 순환 참조라 부름 -> 강한 순환 참조를 해결하기 위해 weak과 unowned를 사용하자!!
Strong
- Strong은 객체를 소유하여 Reference Count가 증가하는 프로퍼티이다.
- 값 지정 시점에 retain이 되고 참조가 종료되는 시점에 release된다.
- 레퍼런스 카운트를 증가시켜 ARC로 인한 메모리 해제를 피하고 객체를 안전하게 사용하고자 할 때 쓰인다.
- 별도의 키워드를 적지 않는 다면 strong이 default값이다.
Weak
- Weak은 객체를 소유하지 않고 주소값만을 가지고 있는 포인터 개념이다.
- 자신이 참조는 하지만 weak 메모리를 해제시킬 수 있는 권한은 다른 클래스에 있다.
- 값 지정시 리테인이 발생하지 않는 다. 따라서 릴리즈도 발생하지 않는다. 그래서 언제 어떻게 메모리가 해제될 지 알 수가 없다.
- 다만 메모리가 해제될 경우 자동으로 레퍼런스가 nil로 초기화를 해준다.
- 그렇기 때문에 weak 속성을 사용하는 객체는 항상 옵셔널 타입이어야한다.
- retain cycle에 의해 메모리가 누수되는 문제를 막기위해 사용되며 Delegate가 그 예시다.
Unowned
- Unowned는 Weak와 매우 비슷한 역할을 한다.
- 차이점으로 Unowned로 선언된 변수는 nil이 될 수가 없다.
- 그러므로 옵셔널로 선언되어서는 안된다.
- 해제된 메모리 영역을 접근하지 않는 다는 확실한 경우에만 사용해야한다.
- 참조 대상이 이미 해제되었는 데 unowned self를 사용하면 런타임 에러인 Unexpectedly found nil while unwrapping an Optional value가 발생한다.
- 즉 객체의 라이프 사이클이 명확하고 개발자에 의해 제어 가능이 명확한 경우에 사용해야한다.
클로저 블럭내에서 [weak self] in 코드를 넣는 방법으로 순환 참조를 해결하는데, 이때 [weak self] in 코드가 하는 역할과 그 이유는?
[weak self]
의 역할로는 ARC가 프로퍼티의 갯수를 카운팅 하지 않도록 만들며 카운팅이 되지 않기에 순환참조가 일어나지 않도록 만드는 역할 한다.
- 그 이유로는 weak 참조는 ARC에 의해 참조되는 인스턴스가 메모리에서 해제 될 때 프로퍼티의 값을 nil로 만들기 때문에 순환 참조가 발생하지 않는다.
[weak self]는 항상 써야할까?
self
가 강한 참조 되지 않는 경우에는 weak self
를 사용할 필요가 없다.