[Swift 기초] 자동참조계수, 강한참조, 약한참조

2022. 4. 4. 15:27Swift 기초 정리

728x90

//공부 기록용 포스팅입니다. 틀린 부분이 있을 경우 댓글로 알려주시면 감사합니다! 😎

 

 

 

0. 메모리 관리

  • heap 영역: 동적으로 메모리를 할당할 때 위치하는 영역, 필요없을 때 소거되는 영역

1. 자동 참조 계수

  • JAVA: 가비지 콜렉터를 사용. 메모리 관리는 편리하나 가비지 콜렉터가 수시로 실행되어 시스템에 부하
  • C, C++: 개발자가 할당한 동적 메모리를 직접 해제해야 함, 에러 가능성 높음
  • 자동참조 관리 기법
    • 프로그램의 메모리 사용을 자동 참조계수를 통해 추적, 관리
    • 필요없는 클래스 인스턴스 메모리를 자동적으로 해제
    • 클래스의 인스턴스에만 적용

1-1. ARC: Automatic Reference Counting

class Person {
    var name: String
    init(name: String){
        self.name = name
        print("\\(name) is being initialized")
    }
    deinit{
        print("\\(name) is being deinitialized")
    }
}

var reference1: Person?
var reference2: Person?
var reference3: Person?

//ARC가 Person 인스턴스 메모리 유지 시작 참조 +1
reference1 = Person(name: "ㄱㅅㄱ")
//ㄱㅅㄱ is being initialized

reference2 = reference1  //참조 +1
reference3 = reference1  //참조 +1 : 참조 카운터 3

reference1 = nil //참조 -1
reference2 = nil //참조 -1
reference3 = nil //참조 -1: Person의 인스턴스 참조가 모두 없어져 ARC가 person 인스턴스 메모리 할당 해제
//ㄱㅅㄱ is being deinitialized

 

2. 강한 참조 순환

  • 인스턴스가 서로 각각 1개 이상의 참조가 순환해서 생겨 ARC가 각각의 인스턴스 메모리를 유지 시작
  • deinit을 제대로 하지 않으면 영원히 강한 참조 순환을 하며 메모리를 차지한다.
class Person {
    let name: String
    var apartment: Apartment?
    init(name: String){
        self.name = name
    }
    deinit{
        print("\\(name) is being deinitialized")
    }
}

class Apartment{
    let number: Int
    var tenant: Person?
    init(number: Int){
        self.number = number
    }
    deinit{
        print("Apartment \\(number) is being deinitialized")
    }
}

var john: Person?
var number73: Apartment?

//Person과 Apartemnt가 1개 이상의 참조가 생겨 ARC가 각각의 인스턴스 메모리 유지 시작
john = Person(name: "Jhon Appleseed")
number73 = Apartment(number: 73)

//Person 인스턴스와 Apartment인스턴스가 서로를 강한 참조
john?.apartment = number73
number73?.tenant = john

//인스턴스를 참조하는 변수는 해제되었지만 내부의 강한 참조는 유지되고 있다.
//이렇게 해제하면 참조할 방법이 없고, 이러한 객체들은 해제되지 않고 남아서 메모리를 차지한다.
//2-1 참고
john = nil
number73 = nil
  • deinit이 출력되지 않음: 인스턴스를 참조하는 변수는 해제되어도 내부의 강한 참조는 해제되지 않음 → 참조를 먼저 해제하기

2-1. 강한 참조 순환 할당 해제하기

var john: Person?
var number73: Apartment?

//Person과 Apartemnt가 1개 이상의 참조가 생겨 ARC가 각각의 인스턴스 메모리 유지 시작
john = Person(name: "Jhon Appleseed")
number73 = Apartment(number: 73)

//Person 인스턴스와 Apartment 인스턴스가 서로를 강한 참조
john?.apartment = number73
number73?.tenant = john

//참조를 먼저 해제하고 인스턴스를 해제하기
john!.apartment = nil
number73!.tenant = nil

john = nil
number73 = nil

//Apartment 73 is being deinitialized
//Jhon Appleseed is being deinitialized

 

3. 강한 참조 순환 방지

3-1. 약한 참조

  • 약한 참조는 인스턴스가 다른 인스턴스를 참조할 때 참조 카운터를 유지하지 않고 참조하는 방식: 참조하던 인스턴스가 할당 해제되면 nil을 할당
    • 레퍼런스를 가지고 동작하지만 인스턴스를 해제해야할 지 판단할 때는 사용하지 않는다.
    • 참조를 하되 강한 참조가 없을 경우 인스턴스는 해제된다.
  • 약한 참조는 참조할 인스턴스가 존재하지 않을 가능성이 있으므로 반드시 옵셔널 타입의 변수로 선언해야 함
  • weak 키워드를 프로퍼티 앞에 사용
    • weak var tenant: Person?
  • 참조 해제 (john!.apartment = nil) 없이 사용할 수 있다.
class Person {
    let name: String
    var apartment: Apartment?
    init(name: String){
        self.name = name
    }
    deinit{
        print("\\(name) is being deinitialized")
    }
}

class Apartment{
    let number: Int
    weak var tenant: Person?
    init(number: Int){
        self.number = number
    }
    deinit{
        print("Apartment \\(number) is being deinitialized")
    }
}

var john: Person?
var number73: Apartment?

//Person과 Apartemnt가 1개 이상의 참조가 생겨 ARC가 각각의 인스턴스 메모리 유지 시작
john = Person(name: "Jhon Appleseed")
number73 = Apartment(number: 73)

//Person 인스턴스는 Apartemnt 인스턴스에 대해 강한 참조를 함
//Apartemnt 인스턴스가 Person인스턴스에 대해 약한 참조를 함
john?.apartment = number73
number73?.tenant = john

//참조 해제가 없음!

//john이 nil이 되면 Person 인스턴스에 대한 강한 참조가 없으므로(= 참조 카운터가 0이므로) ARC가 Person 인스턴스를 할당 해제 시킨다.
//Apartment 인스턴스에 대한 강한 참조가 없어지므로 약한참조인 Apartment 인스턴스도 할당해제된다.
john = nil

//Jhon Appleseed is being deinitialized

(출처: 창원대학교 박동규 교수의 Swift 언어 기초 강좌, 16강 – 자동참조계수, 강한참조, 약한참조)

3-2. 미소유 참조(unowned reference)

  • 참조하는 인스턴스가 언제나 값을 가지고 있다고 간주
    • 옵셔널이 아닌 프로퍼티가 강한 참조를 가지지 않게 하려면 사용
    • 프로퍼티가 nil을 가지지 않는 것을 전제로 unowned reference 사용
  • 반드시 인스턴스를 참조하는게 확실할 때만 사용
  • unowned 키워드를 프로퍼티 앞에 사용
class Employee{
    var name: String
    var team: Team?
    
    init(name: String){
        print("Employee init")
        self.name = name
        self.team = nil
    }
    deinit{
        print("Employee deinit")
    }
}
class Team {
    var name: String
    //Team 클래스의 leader 프로퍼티가 미소유 참조
    //Team 클래스는 항상 leader 프로퍼티를 가져야 한다.(옵셔널 아님)
    unowned var leader: Employee
    
    init(name: String, leader: Employee){
        print("Team init")
        self.name = name
        self.leader = leader
    }
    deinit {
        print("Team deinit")
    }
}

var emp1: Employee? = Employee(name: "ㄱㅅㄱ")
var team1: Team? = Team(name: "ㄱㅅㄱ팀", leader: emp1!)
//Employee init
//Team init

emp1!.team = team1

//강한 참조는 아니므로 참조 사이클이 생기지 않는다.
team1 = nil
emp1 = nil

//Employee deinit
//Team deinit

 

 

참고하였습니다. 감사합니다.

https://www.inflearn.com/course/창원대학교-박동규-교수의-swift-기초-강좌/lecture/1902?tab=note&mm=null 

 

728x90

'Swift 기초 정리' 카테고리의 다른 글

[Swift 기초] 프로토콜  (0) 2022.04.05
[Swift 기초] assert와 guard  (0) 2022.04.05
[Swift 기초] 타입캐스팅  (0) 2022.04.04
[Swift 기초] 인스턴스의 생성과 소멸  (0) 2022.04.02
[Swift 기초] 클래스와 상속  (0) 2022.04.02