Swift

[Swift] ARC에 대해서 알아보자

CalKim 2021. 9. 2. 19:16

안녕하세요. CalKim 입니다.

오늘은 ARC에 대해서 알아보겠습니다~~

사실 어제도 ARC에 대해서 게시글을 쓰다가 말았는데...

다시 공부해서 간결한 내용으로 돌아왔습니다ㅋㅋㅋ

시작합니다.

 

 

 

메모리의 구조

먼저 ARC에 대해 살펴보기 전에 간단히 메모리의 구조에 대해서 살펴보겠습니다.

메모리의 구조

일반적으로 메모리는 위와 같은 구조로 생겼습니다.

 

각각의 영역에는 다음과 같은 정보가 저장됩니다.

 

코드 영역 - 실행할 프로그램의 코드, 제어문, 상수

데이터 영역 - 전역 변수, 정적 변수

힙 영역 - 동적 할당 (런타임에 크기 결정)

스택 영역 - 지역 변수, 매개 변수 (컴파일 타임에 크기 결정)

 

여기에서 이번에 우리가 살펴볼 ARC와 관련된 부분이 힙 영역입니다.

 

 

 

 

객체의 할당

var calKim = Human()

위의 코드를 보겠습니다.

우선 calKim이 전역 변수가 아니라 지역, 혹은 멤버변수라고 가정하겠습니다.

 

이 코드에서는

calKim 이라는 변수가 선언되고, Human() 객체를 할당 받는 두개의 작업이 이루어집니다.

 

변수가 선언되면 스택에 calKim의 공간이 생깁니다.

이 공간은 Human 클래스의 인스턴스를 담는 공간이 아닙니다.

 

Human의 인스턴스는 힙에 생성이 됩니다.

그리고 스택의 calKim은 그 힙의 주소를 가지게 됩니다.

 

 

즉,

변수 a = 객체 x

형태의 코드가 의미하는 바는

a에 x의 주소를 넣어라 입니다.

 

 

그렇다면 다음이 의미하는 코드는 무엇일까요?

calKim = nil

이 코드의 의미는

calKim이 참조하던 그 Human의 인스턴스를 지워라

가 아닙니다.

 

calKim 이 가지고 있던 인스턴스의 참조값을 없애라 입니다.

 

따라서 이와 같은 예제 상황을 확인할 수 있습니다.

class Human {

    var name = ""
    
    init(_ name : String) {
        self.name = name
    }
    
    
}

var cheolsu: Human? = Human("cheolsu")
var cheolsu2 = cheolsu

print(cheolsu?.name) // cheolsu
print(cheolsu2?.name) // cheolsu
print()


cheolsu?.name = "cheol"

print(cheolsu?.name) // cheol
print(cheolsu2?.name) // cheol
print()

cheolsu = nil

print(cheolsu) // nil
print(cheolsu2?.name) // cheol
print()

cheolsu = Human("su")

print(cheolsu?.name) // su
print(cheolsu2?.name) // cheol
print()

 

여기까지 확인하고 이제부터 ARC가 무엇인지 보도록 하겠습니다.

 

 

 

ARC (Automatic Reference Counting)

스위프트에서는 ARC를 통해 메모리를 관리하게 됩니다.

ARC는 Automatic Reference Counting의 약자로,

말 그대로 참조 횟수를 자동으로 카운트 해주는 시스템입니다.

 

ARC는 힙에 있는 인스턴스가 몇 번 참조되는지 세어줍니다.

var calKim: Human? = Human()

에서는 Human의 인스턴스가 만들어진 뒤 calKim에 한 번 할당되었으므로 rc = 1 이겠죠.

 

calKim = nil

위의 코드가 실행되면 calKim 이 더 이상 해당 인스턴스를 참조하지 않으므로,

rc = 0 이 됩니다.

 

rc = 0이 된 인스턴스는 

더 이상 사용되지 않는 인스턴스로 간주하여 메모리에서 해제됩니다.

 

 

그렇다면

var calKim: Human? = Human()
var calKim2 = calKim

위 코드에서는 인스턴스가 두 번 참조 되므로 rc = 2 가 되고,

 

calKim = nil

calKim에 nil을 주면,

인스턴스의 rc = 1 이 되겠죠.

 

결과적으로

calKim2는 해당 인스턴스를 정상적으로 가리키고 있게 됩니다.

 

 

 

이와 같은 방식으로 메모리 해제를 할 때 생길 수 있는 문제인

'순환참조'에 대해서 살펴보겠습니다.

 

 

 

 

순환참조

순환참조는 a가 b를, b가 a를 동시에 참조할 때 발생하는 문제입니다.

우선 다음의 코드 상황을 보도록 하겠습니다.

class Man {
    var name: String
    var gf: Woman?
    
    init(_ name: String) {
        self.name = name
    }
}

class Woman {
    var name: String
    var bf: Man?
    
    init(_ name: String) {
        self.name = name
    }
}

var julie: Woman? = Woman("julie")
var david: Man? = Man("david")

julie?.bf = david
david?.gf = julie

이 때 julie과 david이 가리키는 인스턴스들은 각각 rc가 2가 되겠죠.

 

그러다 julie과 david이 죽었습니다.

julie = nil
david = nil

이 때, julie도 david도 nil을 지정하게 되었으므로,

각각의 인스턴스들이 메모리에서 해제되기를 기대하게 됩니다.

 

하지만,

위의 코드로는 rc 가 2에서 1로 줄어들뿐이므로,

각각의 인스턴스들은 메모리에 잔류하게 됩니다.

단지, 접근할 수 없게 된 것 뿐이죠.

 

이와 같은 문제는 메모리 누수로 이어집니다.

순환 참조로 인한 메모리 누수를 막기 위한 방법으로는 약한 참조가 있습니다.

 

 

 

약한 참조

약한 참조는 다른 객체를 참조할 때,

rc 값을 올리지 않는 참조방식입니다.

 

참고로, 약한 참조의 반대인 강한 참조는

저희가 기본적으로 사용하고 있는

rc 값을 올리는 방식의 참조입니다.

 

약한 참조는 키워드 weak를 사용하여 선언하게 됩니다.

class Man {
    var name: String
    var gf: Woman?
    
    init(_ name: String) {
        self.name = name
    }
}

class Woman {
    var name: String
    weak var bf: Man?
    
    init(_ name: String) {
        self.name = name
    }
}

var julie: Woman? = Woman("julie")
var david: Man? = Man("david")

julie?.bf = david
david?.gf = julie


david = nil
julie = nil

Woman class 의 bf에 weak 를 붙여주었습니다.

그러면 bf를 통해 참조하게 되는 객체의 rc 값은 오르지 않게 됩니다.

 

weak는 순환참조를 일으키는 두 객체 중 한 개에만 붙여주면 됩니다.

이 때, weak가 붙는 객체는 먼저 사라지는 객체입니다.

위의 코드의 경우, david이 먼저 nil이 되므로 Woman의 bf에 weak가 붙게 됩니다.

 

 

 

이렇게 오늘의 게시글을 마치도록 하겠습니다.

내용은 마무리 하기가 애매하네요.

읽어주셔서 감사합니다.

 

 

 

아, 이 내용과 관련된 설명을 자세히 적어준 블로그 링크도 첨부합니다.

저도 보면서 공부하다보니 예제나 흐름을 많이 따라하게 된 것 같아요.

https://babbab2.tistory.com/

https://babbab2.tistory.com/25?category=831129 

 

iOS) 메모리 구조 (Code, Data, Stack, Heap)

안녕하세여~~ 소들입니다 :-))))) 오늘 웬 듣보잡 버그 한 놈이 나왔는데 처음에 메모리 참조 오류인 줄 알고 하루 종일 메모리에 대해서 공부 했는데 버그 원인은 메모리가 아니었음ㅋ (디코딩 네

babbab2.tistory.com

https://babbab2.tistory.com/26?category=831129 

 

iOS) 메모리 관리 (1/3) - ARC(Automatic Reference Counting)

안녕하세요~~ 소들입니다 👀 오늘은 지난 시간 메모리 구조에 이어 Swift를 사용할 때 메모리 관리가 어떤 식으로 되는지에 대해 공부해볼 거예요 :) ARC 면접 단골 질문이라죠? 깔깔 iOS 개발자라

babbab2.tistory.com

https://babbab2.tistory.com/27?category=831129 

 

iOS) 메모리 관리 (2/3) - strong , weak, unowned, 순환 참조

안녕하세여 소들입니다! 저번 포스팅이 길어져서 한번 끊고 가봅니다 :) 뭐 흐름 상 끊어도 될만한 부분이었어서.. ~_~ 이번에 공부할 내용은  뭐 제목에서 써놓은 것처럼 strong weak unowned 순환 참

babbab2.tistory.com

 

 

 

 

* 이 게시글에서 내용 상의 틀린 점 혹은 더 알아야 할 점 등이 있다면  알려주시면 감사히 배우겠습니다