티스토리 뷰

INDEX

1.  ARC(Automatic Reference Counting)

2.강한 참조 싸이클(Strong Reference Cycle)

3. 약한 참조(weak References)

4. 미소유 참조(unowned References)

5. 마무리

 

 

1. ARC(Automatic Reference Counting)

 

ARC는 자동 참조 카운팅으로, Swift에서 자동으로 메모리관리를 해주는 방식이다.

RC(Reference Counting)은 참조타입인 클래스의 인스턴스(객체)에만 적용된다.

ARC의 작동방식

객체(클래스의 인스턴스)가 생성될때마다

객체가 얼마나(몇 번이나) 참조(reference)되고 있는지를 계산(Counting)하는 방식

Swift의 경우, 컴파일러가 실제로

retain() 할당 ---> release() 해제 코드를 삽입한다

이렇게 컴파일러가 메모리 관리 코드를 자동으로 추가해 줌으로써,

프로그램의 메모리 관리에 대한 안정성이 증가한다.

[ARC모델의 기반: 소유정책과 참조카운팅]

   1. 소유정책 - 인스턴스는 하나이상의 소유자가 있는 경우 메모리에 유지됨

             (소유자가 없으면, 메모리에서 제거)

   2. 참조카운팅 - 인스턴스(나)를 가르키는 소유자수를 강한 참조(strong reference)로 가리켜 카운팅

(쉽게 말하면, 인스턴스를 가르키고 있는 RC가 1이상이면 메모리에 유지되고, 0이되면 메모리에서 제거됨)

 

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

//변수 3개 모두 Person?(옵셔널타입으로 정의)라 nil값으로 초기화되어 있다!

var reference1: Person?
var reference2: Person?
var reference3: Person?
//=> Person을 참조하지 않기에 RC는 그대로 0


//Person객체를 생성하여 변수 reference1에 할당!
reference1에서 Person객체에 대한 강한참조 + 1, RC == 1

reference1 = Person(name: "Lee") // RC + 1, RC == 1
// Prints "Lee is being initialized"

//Person객체를 변수 reference1을 통해 다른 2개의 변수에 할당!
//Person객체에 대한 강한참조 + 2, RC == 3

reference2 = reference1 // RC + 1, RC == 2
reference3 = reference1 // RC + 1, RC == 3


reference1 = nil // RC - 1, RC == 2
reference2 = nil // RC - 1, RC == 1
reference2 = nil // RC - 1, RC == 0 	//RC가 0이되었기에, 메모리에서 해제됨
// Prints "Lee is being initialized"	//메모리에서 해제되었기에 소멸자가 호출됨

위 예제를 통해 ARC가 어떻게 작동하는지 알게되었을 것이다. 

하지만 ARC의 강한 참조방식은 두개의 클래스의 인스턴스(객체)가 서로를 강하게 참조할 때

강한 참조 사이클(Strong Reference Cycle)이라는 문제를 발생시킨다.

강한 참조 사이클은 두 객체가 서로를 강하게 참조한다는 점에서 

변수의 참조에 nil을 할당해도 메모리가 해제되지 않는 메모리누수(Memory Leak)가 발생하기 때문이다.

 

2.강한 참조 싸이클(Strong Reference Cycle) -> 메모리 누수 원인

//Person 클래스는 Apartment클래스의 인스턴스 소유
class Person {
    let name: String
    init(name: String) { self.name = name }
    var apartment: Apartment?
    deinit { print("\(name) is being deinitialized") }
}

//Apartment 클래스는 Person클래스의 인스턴스 소유
class Apartment {
    let unit: String
    init(unit: String) { self.unit = unit }
    var tenant: Person?
    deinit { print("Apartment \(unit) is being deinitialized") }
}

var john: Person?
var unit4A: Apartment?

john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")

지금까지의 상황:

스택영역에 저장된 변수 john에 힙에 저장된 클래스 Person 인스턴스 참조,

스택영역에 저장된 변수 unit4A, 힙에 저장된 클래스 Apartment 인스턴스 참조 

Person과 Apartment의 RC는 둘다 1 

 

john!.apartment = unit4A
unit4A!.tenant = john

새로운 상황:

힙에 저장된 john의 apartment변수에 unit4A(Apartment객체)할당 => Apartment 인스턴스 참조

힙에 저장된 unit4A의 tenant 변수에 john(Person객체)할당 => Person 인스턴스 참조

Person과 Apartment의 RC는 둘다 2 (Person인스턴스와 Apartment인스턴스가 서로를 가리킴)

 

nil값을 할당해 참조를 해지해보려 하지만

john = nil
unit4A = nil

//소멸자는 호출되지 않는다.

스택영역의 변수 john과 unit4A의 손은 이미 떠났다...

힙에 저장된 Person인스턴스와 Apartment인스턴스가(지네들끼리) 서로를 가리키며 RC는 1로 유지ㅠㅠ

 

 

해결방법은? 

1. ⭐️Weak References(약한 참조)⭐️

2. Unowned References(미소유 참조)

 

3. 약한 참조(weak References)

1. Weak References(약한 참조)

weak키워드를 사용한다.

약한 참조로 선언하면 참조하고 있는 것이 먼저 메모리에서 해제되기 때문에

ARC는 약한 참조로 선언된 참조 대상이 해지 되면

런타임에 자동으로 참조하고 있는 변수에 nil을 할당한다.

 

/weak키워드 빼고는 강한 순환 참조예제와 똑같다.
class Person {
    let name: String
    init(name: String) { self.name = name }
    var apartment: Apartment?
    deinit { print("\(name) is being deinitialized") }
}

class Apartment {
    let unit: String
    init(unit: String) { self.unit = unit }
    weak var tenant: Person? // weak 키워드❗️
    deinit { print("Apartment \(unit) is being deinitialized") }
}

var john: Person?
var unit4A: Apartment?

john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")

john!.apartment = unit4A
unit4A!.tenant = john

tenant는 Person인스턴스를 약한 참조 하고 있으므로 RC를 증가시키지 않는다. RC == 3

여기서 변수 john(Person 인스턴스 참조중) 에 nil 할당

john = nil
// Prints "John Appleseed is being deinitialized"

그결과 ARC에서 아래 그림과 같이 Person 인스턴스를 메모리에서 해지

이 시점에 변수 unit4A(Apartment 인스턴스 참조중)에 nil 할당

unit4A = nil
// Prints "Apartment 4A is being deinitialized"

unit 4A 변수도 사라지고, Apartment 인스턴스도 메모리에서 해지됨

 

약한 참조(weak references)의 경우, 참조하고 있던 인스턴스가 사라지면, nil로 초기화 되어있다.

Person?클래스 내부에 apartment변수에 weak을 붙여 (weak var apartment: Apartment?)

둘다 약한 참조를 해도 결과는 똑같다.

 

4. 미소유 참조(unowned References)

 

미소유참조도 약한참조처럼 인스턴스를 강하게 참조하지 않는다.

약한 참조와의 차이점은

미소유 참조의 경우 항상 값이 있다고 가정하여

nil로 설정할 수 없다(참조하고 있는 인스턴스를 nil 설정하고 접근하면  에러 발생)

 다른 인스턴스와 같은 수명(lifetime)이거나 수명이 때(longer lifetime) 사용된다

class Dog1 {
    var name: String
    unowned var owner: Person1?    // Swift 5.3 이전버전에서는 비소유참조의 경우, 옵셔널 타입 선언이 안되었음
    
    init(name: String) {
        self.name = name
    }
    
    deinit {
        print("\(name) 메모리 해제")
    }
}

class Person1 {
    var name: String
    unowned var pet: Dog1?
    
    init(name: String) {
        self.name = name
    }
    
    deinit {
        print("\(name) 메모리 해제")
    }
}


var bori1: Dog1? = Dog1(name: "보리1")
var gildong1: Person1? = Person1(name: "홍길동1")


// 강한 참조 사이클이 일어나지 않음
bori1?.owner = gildong1
gildong1?.pet = bori1



// 메모리 해제가 잘됨(사실 이 경우 한쪽만 unowned로 선언해도 상관없음)
bori1 = nil
gildong1 = nil

//보리 메모리 해제
//홍길동 메모리 해제
//보리1 메모리 해제
//홍길동1 메모리 해제

// nil로 설정하고 접근하면 ===> 에러 발생

// 1) 에러발생하는 케이스

//gildong1 = nil
//bori1?.owner   // nil로 초기화 되지 않음 에러 발생


// 2) 에러가 발생하지 않게 하려면

gildong1 = nil
bori1?.owner = nil      // 에러 발생하지 않게 하려면, nil로 재설정 필요 ⭐️
bori1?.owner

 

 

5. 마무리

역시 메모리쪽이 제일 빡세다..

ARC가 어떻게 작동하는지 알고,  

강한참조 싸이클이 어떻게 메모리 누수를 일으키는지 RC(Refrence Counting)를 통해서 설명할 수 있고,

약한 참조와 미소유참조가 그 해결책이 될 수 있음을 제시할 수 있어야

다음의 클로저와 관련된 메모리의 개념들(@escaping, 캡쳐리스트...)을 배울때도 보다 쉽게 받아들일 수 있을 것

 

 

출처

Inflearn Allen님의 강의 노트

Swift 공식문서

야곰님의 스위프트 프로그래밍 3

반응형

'Swift' 카테고리의 다른 글

Swift [Closure 3부]  (0) 2023.03.04
Swift [Closure 2부]  (0) 2023.03.04
Swift [Closure 1부]  (0) 2023.03.01
Swift [Protocol 2부]  (0) 2023.02.27
Swift [Protocol 1부]  (0) 2023.02.26
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/08   »
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
31
글 보관함