반응형
Swift는 ARC ( 자동 참조 계산 )을 사용하여 앱의 메모리 사용을 추적하고 관리한다.
ARC 동작 방식
1. 인스턴스 생성과 메모리 할당
- class의 새로운 인스턴스를 생성할 때 마다 ARC는 해당 인스턴스에 대한 정보를 저장할 메모리 공간을 할당
- 인스턴스의 타입 정보와 프로퍼티의 값이 저장됨.
2. 참조 카운트 관리
- ARC는 사용중인 인스턴스가 메모리에서 해제되지 않도록, 몇개의 프로퍼티, 상수, 변수가 인스턴스를 참조하고 있는지 추적
- 참조 시작 시 : 카운팅 + 1
- 참조 종료 시 : 카운팅 - 1
- 최종적으로 참조 카운트가 0이 되면 인스턴스는 메모리에서 해제
3. 메모리 해제
- 참조 카운트가 0이 된 인스턴스는 더 이상 필요하지 않기 때문에, ARC가 해당 인스턴스가 사용했던 메모리를 해제함.
- 만약 참조 카운트가 0이 되기전에 인스턴스가 해제되면, 이후 그 인스턴스에 접근할 경우 앱이 크래시가 발생할 수 있다.
ARC 동작 시기
- ARC는 컴파일타임에 인스턴스의 참조 카운트를 증가 또는 감소시키는 코드를 자동으로 삽입한다.
Memory Leak
- 메모리 누수
- 사용이 끝난 인스턴스가 메모리에서 해제되지 않고 남아있는 현상.
- 메모리 공간을 불필요하게 사용하게 되면 성능 저하가 발생할 수 있다.
강한순환참조 : Strong Reference Cycle
- iOS에서 자주 발생하는 Memory Leak의 원인 중 하나.
class Person {
let name: String
init(name: String) { self.name = name }
var apartment: Apartment?
deinit { print("\(name) is being deinitialized") }
}
class Apartment {
let unit: String
init(unit: String) { self.unit = unit }
var tenant: Person?
deinit { print("Apartment \(unit) is being deinitialized") }
}
- Person과 Apartment는 서로를 참조하고 있기 때문에 순환 참조가 발생할 수 있다.
var john: Person?
var unit4A: Apartment?
john = Person(name: "John") // RC : 1
unit4A = Apartment(unit: "4A") // RC : 1
- 두 인스턴스를 생성하고 각각 강한참조로 참조하고 있는 상대 ( Default가 Strong이기 때문 )
john!.apartment = unit4A // john의 아파트에는 unit4A를 할당해서 RC가 1 올라서 2가 됨
unit4A!.tenant = john // unit4A의 거주자에는 john을 할당해서 RC가 1 올라서 2가 됨
- Person instance와 Apartment instance가 서로 강한 참조로 사용
john = nil // 참조가 종료되었으므로 Person instance RC에 -1을 함
unit4A = nil // 참조가 종료되었으므로 Apartment instance RC에 -1을 함
// 하지만 Person, Apartment의 instance RC는 2에서 1로 줄었을 뿐입니다.
- Person instance와 Aparment instance의 RC가 1로 사용이 종료되었지만 메모리에서 해제되지 않음.
- 강한 참조로 서로를 참조한다고 하여 강한순환참조라고 한다.
강한순한참조 해결방법
- 참조 카운트를 올리지 않으면 강한순한참조를 방지할 수 있다.
- 참조 카운트를 올리지 않기 위해서는 Strong이 아닌 Weak, unowned를 사용.
class Person {
let name: String
init(name: String) { self.name = name }
weak var apartment: Apartment?
deinit { print("\(name) is being deinitialized") }
}
class Apartment {
let unit: String
init(unit: String) { self.unit = unit }
weak var tenant: Person?
deinit { print("Apartment \(unit) is being deinitialized") }
}
var brody: Person?
var unit100A: Apartment?
brody = Person(name: "brody") // Person instance RC : 1
unit100A = Apartment(unit: "100평 아파트") // Apartment instance RC : 1
brody.apartment = unit100A // Person의 apartment가 weak 이기 때문에 RC가 올라가지 않음 RC : 1
unit100A.tenant = brody // Apartment의 tenant가 weak이기 때문에 RC가 올라가지 않음 RC : 1
brody = nil // Person instance RC 1 감소 -> RC : 0
unit100A = nil // Apartment instance RC에 1 감소 -> RC : 0
// RC가 0이 되어서 Person instance 메모리에서 해제, Apartment instance 메모리에서 해제
객체를 참조하는 방법 3가지
1. strong
- 강한 참조
- Default 값
- 객체를 참조 시작하면 참조 카운트가 1 올라감
- 객체 참조가 종료되면 참조 카운트가 1 내려감
2.weak
- 약한 참조
- 객체를 참조하면 참조 카운트가 변하지 않음
- 객체 참조가 종료되도 참조 카운트가 변하지 않음
- nil을 허용
3.unowned
- 미소유 참조
- 객체를 참조하면 참조 카운트가 변하지 않음
- 객체 참조가 종료되도 참조 카운트가 변하지 않음
- nil이면 크래시 발생.
관련 문서
Documentation
docs.swift.org
반응형
'iOS > Swift' 카테고리의 다른 글
[ Swift ] 비동기 처리와 DispatchQueue.main.async (0) | 2025.04.04 |
---|---|
[ Swift ] Swift 5.9 신기능: 복사 불가능한 타입 (Noncopyable Types) (0) | 2025.03.26 |
[ Swift ] 접근 제어자 (0) | 2025.03.13 |
[ Swift ] 중첩 타입 (0) | 2025.03.13 |
[ Swift ] Override (0) | 2025.02.06 |