✅ ARC란?
Swift에서 메모리 관리를 자동으로 수행하는 방법
✅ 객체의 생명주기
- 객체의 수명은 init 시점에 시작되어 마지막 사용 시점에 종료됩니다
- ARC는 객체의 수명이 종료된 후 해당 객체를 해제합니다
- ARC는 참조 카운트를 통해 객체의 수명을 추적합니다
- Swift 컴파일러는 retain/release 작업을 삽입합니다
- Swift 런타임은 참조 카운트가 0인 객체를 해제합니다
✅ ARC의 동작방식
class Traveler {
var name: String = ""
var destination: String?
}
func test() {
let traveler1 = Traveler(name: "Lily")
// retain
let traveler2 = traveler1 // REFERENCE BEGINS
// release
traveler2.destination = "Big Sur" // REFERENCE ENDS
// release
print("Done traveling")
}
- ARC는 클래스 인스턴스를 기준으로 동작
- 객체가 생성되면 참조 카운트가 1로 설정
- 다른 변수나 상수가 해당 객체를 참조하면 카운트가 증가하고, 참조가 해제되면 카운트가 감소
- 참조 카운트가 0이 되면, ARC는 해당 객체를 메모리에서 해제
Swift에서 객체의 생명 주기는 괄호가 닫힐 때까지 보장되는 C++과 달리 실제 사용 여부에 따라 결정되고, ARC의 최적화에 따라 정해지기 때문에 정확한 시점을 알순 없음
✅ 순환 참조
Traveler와 Account가 서로 참조하고 있음
두 객체의 사용이 끝난 시점에도 서로를 참조하고 있던 참조 카운트 1이 남아있어 메모리가 해지되지 못하고 Memory Leak 발생
✅ Weak과 Unowned
weak | unowned | |
ARC 카운트 증가 | ❌ 증가 안 함 | ❌ 증가 안 함 |
nil 허용 여부 | ✅ Optional | ❌ Non-Optional |
사용 시기 | 참조 대상이 먼저 해제될 수 있을 때 (ex. delegate) | 참조 대상이 자신보다 오래 살아있을 것이 확실할 때 |
class Traveler {
var name: String
var account: Account?
}
class Account {
weak var traveler: Traveler?
var points: Int
func printSummary() {
if let traveler = traveler {
print("\(traveler.name) has \(points) points")
}
}
}
func test() {
let traveler = Traveler(name: "Lily")
let account = Account(traveler: traveler, points: 1000)
traveler.account = account // 이때 객체가 해제된다면,
account.printSummary() // 이미 해제된 객체에 접근하려고 할수 있음
}
weak 참조는 참조 카운트를 증가시키지 않기 때문에 순환 참조를 방지
→ weak으로 선언된 프로퍼티는 항상 Optional(참조하는 객체가 nil일수 있으므로)이어야 하는데, 여기서 문제가 발생할 수도 있음!!
✅ 강한 순환 참조 해결 방법
1) withExtendedLifetime()
func test() {
let traveler = Traveler(name: "Lily")
let account = Account(traveler: traveler, points: 1000)
traveler.account = account
withExtendedLifetime(traveler) {
account.printSummary()
}
}
func test() {
let traveler = Traveler(name: "Lily")
let account = Account(traveler: traveler, points: 1000)
traveler.account = account
account.printSummary()
withExtendedLifetime(traveler) {}
}
func test() {
let traveler = Traveler(name: "Lily")
let account = Account(traveler: traveler, points: 1000)
defer {withExtendedLifetime(traveler) {}}
traveler.account = account
account.printSummary()
}
withExtendedLifetime()을 이용해서 명시적으로 생명주기를 연장할 수 있음
하지만, 잠재적으로 버그 가능성이 있는 곳마다 매번 이 메서드를 사용하는건 피곤하고 가독성도 좋지 않음
2) 재설계하기
위 예시 코드에서 Account 클래스는 단지 Traveler 의 이름만 필요한 것이기 때문에, PersonalInfo 라는 타입을 새로 만들고 여기서 name을 갖게 하면 근본적으로 순환 참조를 해결할 수 있음!!!
https://developer.apple.com/kr/videos/play/wwdc2021/10216/
ARC in Swift: Basics and beyond - WWDC21 - 비디오 - Apple Developer
Learn about the basics of object lifetimes and ARC in Swift. Dive deep into what language features make object lifetimes observable,...
developer.apple.com
'Swift' 카테고리의 다른 글
[Swift] 캡처리스트와 순환참조 (1) | 2025.06.05 |
---|---|
[Swift] 고차함수 더 이상 헷갈리지 않기 (0) | 2025.06.04 |
Struct 접근제어와 Memberwise Initializers (0) | 2025.05.29 |
[Swift]switch문 대신 if, guard 사용하기 (0) | 2025.05.28 |
Struct와 Class 차이(부제: 내가 SwiftUI가 어려웠던 이유) (1) | 2025.05.27 |