일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 스위프트 클로저
- Swift closure
- Raw value and Associated value
- 2022 부스트캠프
- NSSortDescriptor
- 다익스트라 이해
- Swift
- Persistent store Coordinator
- expensive operation
- Associated Value
- 1009번
- 트레일링 클로저
- Clean swift
- persistentStoreCoordinator
- CoreData Concurrency
- iOS Static Library 사용하는방법
- leetcode #01
- Java
- 일급 객체
- dateFormatter
- iOS Static Library
- codability
- CoreData Filter
- NSManagedObject SubClass
- Swift LinkedList
- NSPredicates
- Swift 고차함수
- CoreData Stack
- LightWeight Migration
- CoreData
- Today
- Total
하루를살자
[Swift] 강한 순환 참조 본문
1.0 클래스 프로퍼티간의 강한 순환 참조
위 글에서 정리한듯 ARC 는 Heap 영역에 instance 의 reference count 가 0 일때 자동으로 메모리를 해제해준다.
강한 순환 참조 (strong reference cycle) 은 한쌍의 인스턴스의 가 서로를 참조하고 있어서 reference count 가 0 이 되지않기 때문에 deinit 이 안되는 상황을 말한다. 아래 예제 코드 를 살펴보자
class Phone {
let type: String
var owner: Person?
init(type: String){
self.type = type
}
deinit {
print("Phone Deinit")
}
}
class Person {
let name: String
var phone: Phone?
init(name: String){
self.name = name
}
deinit {
print("Person Deinit")
}
}
var person1: Person? = Person(name: "Kai")
var phone1: Phone? = Phone(type: "iPhone")
person1!.phone = phone1
phone1!.owner = person1
변수 person1 은 Person 을 참조하고 있고, Person 의 프로퍼티인 phone 또한 Phone 의 메모리를 참조하고 있다.
phone1 또한 같은 맥락으로 phone 과 person 을 참조 하게 된다.
현재 Person 과 Phone 의 reference count 는 각각 2가 된다.
여기서 변수 person1 과 phone1 을 nil 로 준다해도 메모리 상에서 person 의 phone 프로퍼티와 phone 의 person 프로퍼티가 서로를 참조하고 있기 때문에 reference count 가 0 이 되지 않고 그대로 메모리에 남아 버리게 된다.
person1 = nil
phone1 = nil
강한 순환 참조 해결법
클래스 타입 안의 프로퍼티 에서 강한 순환 참조가 발생할경우 해결할수 있는 방법은 Weak 혹은 Unowned reference 를 사용하는 방법이 있다. 이 두가지의 방법을 사용하면 강한 참조를 하지않고 참조 사이클을 만들수있다.
Weak reference
인스턴스 의 lifetime 이 비교적 적은곳에 사용할수있다.
예를들면, 사람은 평생동안 휴대폰이 항상있는것이 아니기때문에 아래처럼 phone 이라는 프로퍼티 phone 에 weak 을 사용한다.
class Phone {
let type: String
var owner: Person?
init(type: String){
self.type = type
}
deinit {
print("Phone Deinit")
}
}
class Person {
let name: String
weak var phone: Phone?
init(name: String){
self.name = name
}
deinit {
print("Person Deinit")
}
}
var person1: Person? = Person(name: "Kai")
var phone: Phone? = Phone(type: "iPhone")
person1!.phone = phone
phone!.owner = person1
phone = nil
//Prints
//Phone Deinit
person1 이 참조하는 phone 이 nil 이 되면서 RefereceCount 가 -1 이 된다.
weak 으로 참조 하고 있었기 때문에 ReferenceCounter 가 0 이 되고 phone 은 메모리에서 deinit 이 된다.
Unowned
Weak reference 와 마찬가지로 Unowned reference 또한 강한 참조를 하지 않는다. Weak 과는 다르게, unowned reference 는 인스턴스의 생명주기가 같거나 비슷하다면 사용한다. (swift 5.0 공식 문서에선 Unowned 에 optional 값을 주지못한다고 되어 있는데 xcode 에서 해보니까 옵셔널로 줘도 됐다. 이부분은 나중에 더 자세하게 알아봐야겠다.) https://docs.swift.org/swift-book/LanguageGuide/AutomaticReferenceCounting.html
2.0 클로저로 인한 강한 순환 참조
강한 순환참조는 클래스 안에 해당 인스턴스를 바디안에 캡처하는 클로저를 프로퍼티로 사용할경우 발생할수 있다.
예제 코드를 살펴보자
class Person {
let name: String
var description: String?
lazy var introduce: () -> String = {
return "안녕하세요. 저는 \(self.name) 입니다. "
}
init(name: String){
self.name = name
}
deinit {
print("Person Deinit")
}
}
person 객체 안에 자신을 소개하는 introduce 라는 클로저가 lazy var 로 프로퍼티로 할당되어 있다.
이 클로저는 self.name 을 사용하는데, 이때 바디에서 self 즉, Person 객체 를 캡처 해서 사용하게 된다.
이때 사용 되는 "self" 는 강한참조를 하게 된다.
var person1: Person? = Person(name: "Kai")
//1
print(person1?.introduce())
//2
person1 = nil
person1 의 introduce 클로저가 실행되면 referenceCounter 는 + 1 이 된다.
이후 person1 을 nil 로 할당하여도 메모리에서 introduce 가 Person 을 참조 하고 있으므로 자동 해제가 안된다.
강한 순환 참조 해결법
클로저 와 상위 객체사이에서 강한 순환참조가 발생하는 경우 또한 위에서 설명한 weak, unowned reference 를 사용해서 문제를 해결할수 있다.
class Person {
let name: String
var description: String?
lazy var introduce: () -> String = {
[weak self] in
return "안녕하세요. 저는 \(self?.name) 입니다. "
}
init(name: String){
self.name = name
}
deinit {
print("Person Deinit")
}
}
var person1: Person? = Person(name: "Kai")
print(person1?.introduce())
person1 = nil //Person Deinit"
'Swift' 카테고리의 다른 글
[Swift] Closure 의 Capture, CaptureList (0) | 2022.08.06 |
---|---|
ARC (Automatic Reference Counter) (0) | 2022.08.06 |
[Swift] LinkedList (0) | 2022.08.05 |
[Swift] Expansive DateFormatter : 어떻게 사용해야할까? (0) | 2022.06.09 |
Swift - Closure (2) : Trailing Closure (0) | 2022.02.27 |