Swift: strong / weak / unowned / 순환참조

Wooyo·2023년 10월 25일
0

Swift / 메모리관리

목록 보기
2/2
post-thumbnail

참고 사이트 : https://babbab2.tistory.com/27

1. Strong (강한 참조)

  • 인스턴스 주소값이 변수에 할당 될 때, RC값이 증가하는 경우

2. 순환 참조

  • 아래와 같이 Man은 Woman 타입 프로퍼티 / Woman은 Man 타입 프로퍼티가 존재
class Man {
	var name: String
    var grilfriend: Woman?
    
    init(name: String){
    	self.name = name
    }
    
    deinit {
    	print("Man deinit")
    }
}

class Woman {
	var name: String
    var boyfriend: Man?
    
    init(name: String) {
    	self.name = name
    }
	
    deinit {
    	print("Woman deinit")
    }
}
  • 아래와 같은 인스턴스 생성
var jimin: Man? = Man(name: "Jimin") // strong, Man RC : 1
var minjung: Woman? = Woman(name: "minjung") // strong, Woman RC : 1
  • 아래와 같이 girlfriend / boyfriend 지정하게 되면 서로의 참조하게 되면서 RC값이 증가
jimin?.girlfriend = minjung // Woman RC : 2
minjung?.boyfriend = jimin // Man RC : 2

3. 순환참조 문제점

  • 아래와 같이 nil 지정
jimin = nil // Man deinit이 호출되지 않음
minjung = nil // Woman deinit이 호출되지 않음
// 둘 다 힙 영역의 메모리에서 존재
  • 둘 다 각각의 strong으로 -1 씩되어 힙 영역 RC 값 1로 존재
  • 두 변수 모두 nil로 지정이 되어 인스턴스에 접근 할 수 있는 방법이 없어 메모리 해제 불가능
  • 즉 memory leak 발생
  • strong을 사용해 순환참조에 문제가 생긴 경우 -> 강한 순환 참조

4. Weak (약한 참조)

  • 강한 순환 참조를 해결하기위한 참조법중 하나
  • 인스턴스를 참조할 시 RC 값을 증가시키지 않는다
  • 인스턴스가 메모리 해제된 경우 자동 nil 할당으로 메모리가 해제
  • 강한 순환 참조는 서로를 strong 참조하고 있는 경우로 둘 중 한 쪽을 weak 선언
class Man {
	var name: String
    weak var grilfriend: Woman?
    
    init(name: String){
    	self.name = name
    }
    
    deinit {
    	print("Man deinit")
    }
}

class Woman {
	var name: String
    var boyfriend: Man?
    
    init(name: String) {
    	self.name = name
    }
	
    deinit {
    	print("Woman deinit")
    }
}
  • 순환 참조를 일으키는 코드를 실행
var jimin: Man? = .init(name: "jimin") // Man RC : 1
var minjung: Woman? = .init(name: "minjung") // Woman RC : 1

jimin.grilfriend = minjung // Woman RC : 1
minjung.boyfriend = jimin // Man RC : 2
  • stack 영역 해제시
jimin = nil
minjung = nil

// nil 할당 순간 각각 RC 1 씩 감소 Man RC : 1, Woman RC : 0
// RC가 0이 된 Woman Instance가 메모리에서 해제 Man RC : 0


// Woman Deinit

// weak로 선언된 girlfriend 참조 인스턴스가 메모리에서 해제되어 girlfriend 값이 nil로 할당
// Man RC : 0 이 되어 메모리에서 해제
// deinit 작동

// Man Deinit
  • 둘 중 수명이 더 짧은 인스턴스를 가르키는 부분을 약한 참조로 선언

5. Unowned (미소유 참조)

  • unowned 또한 강한 참조 해결 가능
  • unowned 또한 RC 값 증가시키지 않음
  • unowned 차이 : 인스턴스를 참조하는 도중 해당 인스턴스가 메모리에서 사라질 일이 없다고 확신하는 경우 사용
  • 참조하던 인스턴스가 메모리에서 해제된 경우 nil 할당 받지 못하고 해제된 메모리 주소값을 계속 들고 있게 된다.
  • unowned 으로 선언된 변수가 가리키던 인스턴스가 메모리에서 먼저 해제된 경우, 접근하려 하면 에러가 발생
class Man {
    var name: String
    unowned var girlfriend: Woman?
    
    init(name: String) {
        self.name = name
    }
    deinit { print("Man Deinit!") }
}
 
class Woman {
    var name: String
    var boyfriend: Man?
    
    init(name: String) {
        self.name = name
    }
    deinit { print("Woman Deinit!") }
}
  • minjung 인스턴스의 수명이 더 오래간다는 가정으로 unowned 선언 -> weak와 같은 RC 생성
  • minjung 인스턴스가 먼저 해제된다면 다음과 같은 상황 발생
minjung = nil

// Man RC의 grilfriend 프로퍼티가 nil을 할당 받지 못해 해제된 메모리 주소값을 갖고 있게됨


jimin.girlfriend // girlfriend 프로퍼티 접근시 시스템 에러 발생
  • 보통 수명이 더 긴 인스턴스를 가르키는 객체를 미소유 참조로 선언
profile
Wooyo의 개발 블로그

0개의 댓글