궁금한 내용을 검색해보세요!
이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.
서근 개발노트
티스토리에 팔로잉
SWIFT/Grammar

Swift : 고급문법 [ARC 메모리 관리 1 - 강한 참조]

서근
QUOTE THE DAY

-
Written by SeogunSEOGUN

반응형

본 게시글은 yagom님의 Swift 프로그래밍 3판을 참고하여 작성되었습니다.

 

ARC

참조 타입은 하나의 인스턴스가 참조를 통해 여러 곳에서 접근하기 때문에 언제 메모리에서 해제되는지가 가장 중요하다. 인스턴스가 적절한 시점에 메모리에서 해제되지 않는다면 한정적인 메모리 자원을 낭비하게 되고, 이것은 성능 저하로 이어진다.

 

Swift는 프로그램의 메모리 사용을 관리하기 위해서 메모리 관리 기법인 ARC(Automatic Reference Counting)를 사용한다.

TIP
 
 

ARC와 값 타입
ARC가 관리해주는 참조 횟수 계산(Reference Counting)은 참조 타입인 클래스의 인스턴스에만 적용된다. 구조체나 열거형은 값 타입으로 참조 횟수 계산과 무관하다. 즉, 값 타입은 다른 곳에서 참조하지 않기 때문에 ARC로 관리할 필요가 없다.

ARC는 자동으로 메모리를 관리해주는 방식이다. 메모리 관리는 Swift에서 자동으로 작동하기 때문에, 개발자는 메모리 관리에 대해 신경을 덜 쓸 수 있다. ARC는 더 이상 필요하지 않은 클래스의 인스턴스를 메모리에서 해제하는 방식으로 동작한다.

  • 자동으로 메모리 관리해준다.
  • 객체에 대한 참조 카운트를 관리하고 0이 되면 자동으로 메모리 해제한다.
  • 런타임에 계속 실행되는게 아닌, 컴파일타임에 실행한다.
  • 하지만 retain cycle에는 유의해야 한다.
  • Objective-C에서는 MRC, 즉 수동으로 메모리 관리를 했다.

ARC 인스턴스 생성 및 해제

Swift에서 ARC를 이용해 자동으로 메모리 관리를 받기 위해서는 몇 가지 규칙을 알아야 한다. 그 이유는 컴파일과 동시에 인스턴스를 메모리에서 해제하는 시점이 ARC를 결정하기 때문이다.

  1. ARC는 클래스의 인스턴스를 생성할 때마다 그 인스턴스에 대한 정보를 저장하기 위한 메모리 공간을 따로 또 할당함
    • 이 메모리 공간에는 인스턴스의 타입 정보와 관련된 저장 프로퍼티의 값을 등을 저장한다.
    • 그 후, 인스턴스가 더 필요 없는 상태가 되면 이 공간을 다른 용도로 활용하기 위해 ARC가 메모리에서 인스턴스를 삭제한다.
  2. 만약 아직 더 사용해야 하는 인스턴스를 메모리에서 해제시킨다면
    • 인스턴스의 프로퍼티에 접근하거나, 인스턴스의 메서드를 호출할 수 없다.
    • 만약 강제로 접근하게 된다면 앱이 강제 종료된다.

따라서 ARC는 해당 인스턴스를 참조하는 다른 인스턴스의 프로퍼티나 변수, 상수 등의 개수를 세고, 해당 인스턴스에 대한 활성 참조가 1개 이상 존재하는 한 인스턴스를 메모리에서 없애지 않는다.

강한 참조

인스턴스가 언제 메모리에서 해제될지 예측 가능하도록 ARC에 적용되는 몇 가지 규칙을 알아야 보려 한다. 그리고 이 규칙을 알지 못할 때 벌어질 수 있는 문제점과 해결방안도 알아볼까 한다.

 

인스턴스를 메모리에 유지시키려면 ARC가 해당 인스턴스를 해제하지 않고 유지해야 하는 명분을 제공해야 하는데, 이것이 바로 강한 참조(Strong Reference)이다. 

 

인스턴스는 참조 횟수가 0이 되는 순간 메모리에서 해제된다.

인스턴스를 다른 인스턴스의 프로퍼티나 변수, 상수 등에 할당할 때 강한 참조를 사용하게 되면 참조 횟수가 +1 증가 하게 된다.

 

참조의 기본은 강한참조 이기 때문에 별도의 식별자를 명시하지 않으면 강한참조를 한다.

참조

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

Person 클래스는 인스턴스의 name 프로퍼티를 초기화하고 프린트하는 이니셜라이저와 인스턴스가 해제되었음을 알리는 디이니셜라이저를 가지고 있다.

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

그리고 3개의 Person? 옵셔널 타입 변수가 선언되어 있으며, 해당 변수들은 옵셔널 타입이기 때문에 아직은 Person 인스턴스를 참조하고 있지 않고, nil 값으로 초기화된 상태이다.

 

이제 새로운 Person 인스턴스를 생성해서 위 세 개의 변수 중에 하나에 넣어보면

reference1: Person(name: "서근")
// 서근 is being initialized
// 인스턴스 참조 횟수 : 1

 

새로운 Person 인스턴스는 처음 메모리에 생성되면 이니셜라이저가 호출되며 메시지를 출력한다. 그 이후 reference1에 할당된 Person 클래스 타입의 인스턴스는 강한 참조로 reference1에 할당되므로, Person인스턴스의 참조 횟수가 +1 증가된다.

reference2 = reference1
// 인스턴스 참조 횟수 : 2
reference3 = reference1
// 인스턴스 참조 횟수 : 3

그 후, reference2에 강한참조로 할당되어 참조 횟수 +1, 이 인스턴스가 reference3에 할당할 때도 참조 횟수 +1 이 증가한다. 그 결과 총 인스턴스 참조 횟수가 3이 된다.

 

이제 하나의 Person 인스턴스가 3개의 변수에 강한 참조가 되고 있고, 참조 횟수는 3이 됐다. 따라서 계속 메모리에 살아있게 된다.

해제

reference3 = nil  // 인스턴스 참조 횟수 : 2
reference2 = nil  // 인스턴스 참조 횟수 : 1
reference1 = nil  // 인스턴스 참조 횟수 : 0
// 서근 is being deinitialized

변수에 차례로 nil을 할당하면 참조 횟수가 감소한다.

 

마지막에 참조되었던 reference3에서 제일 먼저 인스턴스 참조를 그만두었기 때문에 참조 횟수는 1 감소하여 2가 됐고, 마찬가지로 reference2reference1에서 순차적으로 참조를 그만두면 참조 횟수가 -1 씩 차감되어 0이 된다.

 

이 참조 횟수가 0이 되는 그 순간 인스턴스는 ARC 규칙에 의해 메모리에서 해제되며 메모리에서 해제되기 직전!! 디이니셜라이저를 호출한다.

 

 

읽어주셔서 감사합니다 🤟

 

 


잘못된 내용이 있으면 언제든 피드백 부탁드립니다.


서근


위처럼 이미지 와 함께 댓글을 작성할 수 있습니다.