-
들어가며
아이폰 앱 개발을 하다 보면 , 분명 ViewDidDisappear가 되었는데도 deInit 이 콜이 안되는 경우가 저에겐 빈번히 일어 났습니다. 대충 문제를 짐작하여 해결하려고 하였지만, 막상 문제를 찾으려고 할 때는 문제점을 명확히 잘 찾지 못 했습니다.
그 이유는 제 자신이 ARC의 개념에 미흡하게 알고 있어서 그런 것 같습니다. 이러한 문제 점을 고치기 위해 글을 쓰게 되었습니다.
ARC란 무엇일까?
Swift uses Automatic Reference Counting (ARC) to track and manage your app’s memory usage. In most cases, this means that memory management “just works” in Swift, and you do not need to think about memory management yourself. ARC automatically frees up the memory used by class instances when those instances are no longer needed.
- Automatic Reference Counting의 줄임말로 , 여러분의 앱 메모리 사용을 관리한다고 합니다.
- Compile Time에서 객체에 대한 참조 카운트를 관리하고 0이 되면 메모리 해제를 시켜 줍니다.
- 참조 타입인 Class에서만 사용 가능합니다.
- 한마디로 말해 , 스위프트에서는 메모리 관리를 제공한다는 것 입니다.
- 개발자들이 메모리 관리에 대해서 생각할 필요 없도록 합니다. (그러나 특정 부분에서는 메모리 관리를 신경 써야 합니다. 자세한 내용은 아래 나와있습니다.)
- ARC는 자동적으로 어떠한 인스턴스들이 더 이상 사용되지 않을 경우에 메모리를 해제 합니다.
ARC는 어떻게 동작될까?
Every time you create a new instance of a class, ARC allocates a chunk of memory to store information about that instance. This memory holds information about the type of the instance, together with the values of any stored properties associated with that instance.
Additionally, when an instance is no longer needed, ARC frees up the memory used by that instance so that the memory can be used for other purposes instead. This ensures that class instances do not take up space in memory when they are no longer needed.
However, if ARC were to deallocate an instance that was still in use, it would no longer be possible to access that instance’s properties, or call that instance’s methods. Indeed, if you tried to access the instance, your app would most likely crash."특정 인스턴스" 를 = "A 인스턴스"라고 하겠습니다.
1. 클래스의 A 인스턴스를 만들면 ARC는 해당 인스턴스의 정보를 저장하기 위해 메모리에 할당합니다.
2. 해당 메모리는 A 인스턴스의 정보와 A 인스턴스와 관련된 저장 프로퍼티들의 값들을 가지고 있습니다.
3. 게다가 , A 인스턴스가 더 이상 필요하지 않다면, ARC는 A 인스턴스를 저장하고 있던 메모리를 해제 합니다.
4. 이로 인해 , A를 저장하고 있던 메모리는 다른 목적으로 사용 될 인스턴스를 저장할 수 있습니다.
5. 이 말은 즉, 클래스 인스턴스가 더 이상 사용되어지지 않을 때 , 메모리에 공간을 차지하지 않습니다.
만약, 현재 사용중인 A 인스턴스를 ARC가 A 인스턴스의 메모리를 해제할 경우 , A 인스턴스에 접근 할 수 없을 뿐더러, 해당 인스턴스의 메서드 또한 접근이 불가하다, 만약 접근한다 할지라도 앱 충돌이 일어날 가능성이 크다고 합니다.
To make sure that instances don’t disappear while they are still needed, ARC tracks how many properties, constants, and variables are currently referring to each class instance. ARC will not deallocate an instance as long as at least one active reference to that instance still exists.
-> A 인스턴스의 메모리 해제를 막기 위해서 한가지 방법이 있습니다. 그 방법은 properties , constants 그리고 variables들이 각각의 클래스 인스턴스를 현재 얼마나 많이 참조하고 있는지 카운팅하여 , 적어도 1개 이상의 클래스를 참조한다면 A 인스턴스는 메모리에서 해제되지 않습니다.
To make this possible, whenever you assign a class instance to a property, constant, or variable, that property, constant, or variable makes a strong reference to the instance. The reference is called a “strong” reference because it keeps a firm hold on that instance, and does not allow it to be deallocated for as long as that strong reference remains.
-> 카운팅을 하기 위해선 각각의 변수들을 선언할 때 , strong reference로 설정해야 합니다. strong reference는 해당 인스턴스를 계속 잡고 있습니다. (강력하게 , 굳건하게) 그리고 strong reference가 남아있는 한 메모리 해제 되는 것을 방지합니다.
ARC 예제는 어떤게 있을까?
-> 해당 예제는 ARC 동작을 보여줄 것 입니다.
Person Class
-> constannt property인 name
Person을 optional로 선언한 3 개의 referennces
3 개의 references 들은 optional type으로 지정 되어 있으므로 전부 nil로 자동 초기화 됩니다.
1. reference1: Person? = Person(name: "Junsmust")
2. reference2: Person? = reference1
3. reference3: Person? = reference1
이 상황을 그림으로 표현해 보겠습니다.
ref1 = Person(name: "John Appleseed") 이 실행되면 "Person 클래스의 init 매서드가 호출되며 , John Appleseed is being initialized"가 샐행 됩니다.
그 이유는 New Person instance가 Ref1에 할당 되었기 때문 입니다.
1. ref1 = Person(name: "John Appleseed") 가 호출 될 때 ARC 카운트는 1이 됩니다.
2. ref2 = ref1
3. ref3 = ref2
2번 과 3번은 ref1 을 참조 함으로써 같은 주소를 바라보고 있습니다. 이로 인해
2번 ref2 = ref1 실행시 ARC 2 (Person)로 증가하게 되고
3번 ref3 = ref2 실해시 ARC 3(Person) 으로 증가 됩니다.
만약 ref1 과 ref2를 nil로 할당하여 dealloc을 할 수 있다고 생각한다면 큰 오산일 것 입니다.
그 이유는 ref1 과 ref2 를 nil 처리 할지라도 ARC 카운트는 여전히 1을 가지고 있습니다.
마지막으로 ref3을 nil을 할당한다면 Deinit이 호출되며 Person 객체가 메모리에서 해제 될 것 입니다.
Strong Reference Cycle Between Class Instances
This can happen if two class instances hold a strong reference to each other, such that each instance keeps the other alive. This is known as a strong reference cycle
서로가 서로의 인스턴스를 가지고 있다면 ARC는 절대 0이 되지 않아 dealloc이 되지 않습니다. 이런 현상은 strong reference cycle이라고알려져 있습니다.
You resolve strong reference cycles by defining some of the relationships between classes as weak or unowned references instead of as strong references. This process is described in Resolving Strong Reference Cycles Between Class Instances. However, before you learn how to resolve a strong reference cycle, it’s useful to understand how such a cycle is caused.
week 와 unowned 를 사용하여 strong reference cycles를 해결할 수 있습니다. 순환 문제를 어떻게 해결하는지 배우는 것 보다 어떻게 순환이 일어났는지 이해하는 것이 좋을 것 입니다.
strong reference cycle 왜 일어날까?
Person 클래스는 name 과 apartment라는 optional 변수를 가지고 있습니다. 그 이유는 사람이라고 해서 아파트를 다 가지고 있는건 아니기 떄문이죠.
Apartment 클래스는 unit 과 tenant라는 optional 변수를 가지고 있습니다. 그 이유는 아파트가 꼭 새입자를 가지고 있는건 아니기 때문이죠.
var john: Person?
var unit4A: Apartment?
john = Person(name: "John Appleseed") // john은 Person 객체를 참조하므로 Person 의 ARC: 1
unit4A = Apartment(unit: "4A") // unit4A는 Apartment 객체를 참조하므로 Apartment 의 ARC: 1
John!.apartment = unit4A // john가 unit4를 참조하므로 Apartment ARC는 2로 증가되고
unit4A!.tenant = john // unit3의 tenant가 john을 참조하므로 Person ARC는 2로 증가 됩니다.
만약, john과 unit4A에 nil을 할당하여도 , 각각의 ARC는 0이 되지 않아 dealloc 되지 않음을 발견할 수 있을 겁니다.
이런한 상황이 강한 참조의 순환 입니다. 이로 인하여 apartment와 tenant의 메모리들은 어딘가에 남아있게 되고 이는
메모리 누수를 발생시키게 된다고 합니다.
다음 순서에는 week 와 unowned 을 사용하여 순환 참조를 대처하는 방법에 대해서 알아보도록 하겠습니다.
봐주셔서 감사합니다.
'ios' 카테고리의 다른 글
로컬 푸시 알림 기능 구현 (Local Notification) (0) 2020.12.17 ARC: Resolving Strong Reference Cycles Between Class Instances (0) 2020.09.07 네트워크 통신 모듈화 with Alamofire (0) 2020.07.21 APNs 사용법 (0) 2020.07.20 Property with type 'Any' does not conforms with codable protocol (0) 2020.07.13