[iOS] ARC (Automatic Reference Counting)

2025. 1. 19. 19:01iOS

반응형

ARC(Automatic Reference Counting)는 Swift와 Objective-C에서 메모리 관리를 자동화하기 위해 사용되는 메커니즘입니다. 메모리 관리는 애플리케이션 개발에서 중요한 과제 중 하나로, 메모리 누수를 방지하고 안정적으로 동작하도록 보장합니다. ARC는 런타임에서 객체의 **사용 횟수(Reference Count)**를 추적하여, 더 이상 사용되지 않는 객체를 자동으로 메모리에서 해제합니다.

1. ARC의 기본 동작 원리

ARC는 객체에 대한 참조를 관리합니다. 각 객체는 메모리에서 얼마나 많은 참조를 받고 있는지를 나타내는 **참조 카운트(Reference Count)**를 가지고 있습니다. 다음은 기본적인 동작 원리입니다:

  • 객체 생성: 새 객체를 생성하면 ARC는 객체의 참조 카운트를 1로 설정합니다.
  • 참조 증가: 다른 변수나 상수가 객체를 참조하면 참조 카운트가 1씩 증가합니다.
  • 참조 감소: 객체에 대한 참조가 해제되거나 소멸되면 참조 카운트가 1씩 감소합니다.
  • 메모리 해제: 참조 카운트가 0이 되면 ARC는 객체를 메모리에서 자동으로 해제합니다.

예제

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

var person1: Person? = Person(name: "Alice") // 참조 카운트: 1 
var person2: Person? = person1 // 참조 카운트: 2 
person1 = nil // 참조 카운트: 1 
person2 = nil // 참조 카운트: 0 -> 메모리 해제

출력:

Alice is initialized 
Alice is deinitialized

2. ARC의 장점

  • 자동화된 메모리 관리: 개발자가 직접 메모리 관리 코드를 작성할 필요가 없으므로 실수를 줄일 수 있습니다.
  • 성능 최적화: 수동 메모리 관리보다 효율적이고 안전합니다.
  • 편의성: 코드 작성이 간결하며, 메모리 관리의 복잡성을 숨겨줍니다.

3. ARC와 참조 종류

ARC는 객체 간의 참조 관계를 추적하며, 세 가지 주요 참조 유형을 제공합니다.

1) 강한 참조(Strong Reference)

  • 기본 참조 유형으로, 객체가 강한 참조로 참조되면 참조 카운트가 증가합니다.
  • 객체 간 순환 참조(Strong Reference Cycle)가 발생할 수 있어 주의가 필요합니다.

2) 약한 참조(Weak Reference)

  • 객체를 참조하되 참조 카운트를 증가시키지 않습니다.
  • 객체가 메모리에서 해제되면 약한 참조는 자동으로 nil로 설정됩니다.
  • weak 키워드를 사용하며, 옵셔널로 선언해야 합니다.
 
class Owner { 
	var name: String 
    var pet: Pet? 
    
    init(name: String) { 
    	self.name = name 
    } 
    deinit { 
    	print("\(name) is deinitialized") 
    }
} 

class Pet { 
	var name: String 
    weak var owner: Owner? // 약한 참조로 선언 
    
    init(name: String) { 
    	self.name = name 
    }
    
    deinit { 
    	print("\(name) is deinitialized") 
    }
} 

var owner: Owner? = Owner(name: "John") 
var pet: Pet? = Pet(name: "Buddy") 
owner?.pet = pet pet?.owner = owner // 약한 참조로 순환 참조 방지 
owner = nil // 참조 카운트가 0이 되어 메모리 해제 pet = nil

출력:

John is deinitialized 
Buddy is deinitialized
 

3) 미소유 참조(Unowned Reference)

  • 참조 카운트를 증가시키지 않는다는 점에서 약한 참조와 유사하지만, 객체가 해제된 후에도 nil로 설정되지 않습니다.
  • 객체가 메모리에서 해제되었을 때 참조하려 하면 런타임 에러가 발생할 수 있습니다.
  • 생명 주기가 부모와 자식 관계처럼 서로 강하게 연결된 경우에 사용됩니다.
class CreditCard { 
	var number: Int 
    unowned let customer: Customer // 미소유 참조 
    
    init(number: Int, customer: Customer) { 
    	self.number = number 
        self.customer = customer 
    }
    
    deinit { 
    	print("CreditCard #\(number) is deinitialized") 
    }
} 

class Customer { 
	var name: String 
    var card: CreditCard? 
    
    init(name: String) { 
    	self.name = name 
    } 
    
    deinit { 
    	print("\(name) is deinitialized") 
    }
}

var customer: Customer? = Customer(name: "Alice") 
customer?.card = CreditCard(number: 1234, customer: customer!) 
customer = nil // 두 객체 모두 메모리에서 해제

출력:

Alice is deinitialized 
CreditCard #1234 is deinitialized

4. ARC의 문제점과 해결 방법

1) 순환 참조(Strong Reference Cycle)

강한 참조만 사용할 경우 두 객체가 서로를 강하게 참조하여 메모리에서 해제되지 않는 문제가 발생합니다.

해결 방법:

  • 약한 참조(weak) 또는 미소유 참조(unowned)를 사용하여 순환 참조를 방지합니다.

2) 클로저와 강한 참조

클로저는 객체의 속성이나 메서드를 캡처할 때 강한 참조를 생성하므로 순환 참조를 유발할 수 있습니다.

해결 방법:

  • **캡처 목록(Capture List)**를 사용하여 약한 참조 또는 미소유 참조로 캡처합니다.
 
class Example { 
	var message = "Hello" 
    lazy var closure: () -> Void = { [weak self] in 
    	print(self?.message ?? "No message") 
    } 
    
    deinit { 
    	print("Example is deinitialized") 
    }
} 

var example: Example? = Example()
example?.closure() // "Hello" 
example = nil // 메모리 해제

출력:

Hello 
Example is deinitialized

5. ARC와 멀티스레딩

ARC는 멀티스레딩 환경에서도 안정적으로 동작하지만, 다음과 같은 상황에 주의해야 합니다:

  • Race Condition: 여러 스레드에서 동일한 객체를 참조하거나 해제할 때 동기화가 필요합니다.
  • Thread Safety: DispatchQueue나 NSLock 등을 사용해 객체 접근을 제어해야 합니다.

6. ARC와 Instruments

메모리 누수 및 참조 카운트 문제를 디버깅하기 위해 InstrumentsLeaksAllocations 도구를 사용할 수 있습니다.


결론

ARC는 iOS와 macOS 애플리케이션 개발에서 메모리 관리를 단순화하며, 개발자의 부담을 크게 줄여줍니다. 하지만 강한 참조 순환과 같은 문제를 방지하기 위해 약한 참조와 미소유 참조를 적절히 활용하고, 클로저 캡처와 멀티스레딩 환경에서 신중한 설계가 필요합니다. ARC의 기본 원리를 이해하고 올바르게 활용하면 효율적이고 안정적인 앱을 개발할 수 있습니다.

반응형

'iOS' 카테고리의 다른 글

[iOS] Concurrency(동시성)  (1) 2025.01.19
[iOS] Retain Cycle (참조 순환)  (1) 2025.01.19
[iOS] 카메라(Camera)/앨범(Album) 권한  (0) 2025.01.13
[RxSwift] PublishSubject  (0) 2025.01.10
[RxSwift] BehaviorSubject  (0) 2025.01.10