ARC(Auto Reference Counting)
- 자동 레퍼런스 카운트 관리
- 객체의 레퍼런스 카운트 관리 코드 자동 생성(컴파일)
- 래퍼런스 타입(Class)객체에만 적용
- 벨류 타입(구조체, Enum) 객체에는 미적용
객체 생성 -> 메모리 차지 -> 메모리 공간의 제약 -> 메모리 관리, 객체가 많아지면 메모리 사용량이 증가하게됨. 이때 적절하게 메모리 해제를 해주어야 함
var ptr: Myclass? = MyClass() // Reference Count 0 -> 1
ptr = nil // Reference Count 1 - > 0
객체 해제 확인 하기
class MyClass {
deinit {
print("객체가 메모리에서 해제")
}
}
var obj : MyClass! = MyClass() // 객체 생성
obj = nil // 객체 해제
객체가 해제되면 deinit 매서드가 호출되서 콘솔창에 작성해놓은 스트링이 출력됩니다.
콜렉션과 소유권
- 콜렉션에 객체 저장: 콜렉션이 객체 소유
- 콜렉션에서 객체 삭제: 소유권 해제
- 콜렉션 객체 해제: 소유권 해제
var obj: MyClass = MyClass()
var array = [obj] // obj 객체의 소유권을 배열이 가지고 있기 때문에, 아래에서 obj의 객체를 해제 해도, 배열의 원소로 있는 obj 객체는 해제되지 않음.
obj = nil // obj 객체 해제,
* 배열에서 삭제 - 소유권 해제
array.remove(at:0)
강한 순환 참조
- 두 개 이상의 관계에서도 가능
- 서로 소유하므로 해제되지 않음(메모리 누수)
- 수동으로 해제되도록 작성 해야함
** 메모리 정상 해제
class Person {
var apartment: Apartment?
deinit {
print("Person 객체 해제")
}
}
class Apartment {
var tenant: Person?
deinit {
print("Apartment 객체 해제")
}
}
var man: Person? = Person()
var home: Apartment? = Apartment()
man = nil // Person 객체 해제
home = nil // Apartment 객체 해제
** 강한 순환 참조
class Person {
var apartment: Apartment?
deinit {
print("Person 객체 해제")
}
}
class Apartment {
var tenant: Person?
deinit {
print("Apartment 객체 해제")
}
}
var man: Person? = Person()
var home: Apartment? = Apartment()
man?.apartment = home
home?.tenant = man
man = nil // 해제 메세지 없음. 하지만 man 객체 는 nil
home = nil // 해제 메세지 없음. 하지만 home 객체 는 nil
- > 메모리 누수
- > 해당 메모리 누수를 해결하기위해서는, 참조한 객체들을 직접 해제 해주어야함.
** 직접 해제
class Person {
var apartment: Apartment?
deinit {
print("Person 객체 해제")
}
}
class Apartment {
var tenant: Person?
deinit {
print("Apartment 객체 해제")
}
}
var man: Person? = Person()
var home: Apartment? = Apartment()
man?.apartment = home
home?.tenant = man
man?.apartment = nil
home?.tenant = nil
man = nil // Person 객체 해제
home = nil // Apartment 객체 해제
약한 참조
- 강한 순한 참조 문제 해결 하기
- 객체를 소유하는 강한 참조만 사용하면 문제가 될수 있음
- 객체를 소유하지 않는 약한참조(Weak Reference) 사용
- 객체를 소유하지 않는 포인터:
weak, unowned
- weak
- 참조하던 객체가 해제되면 자동 nil -> 옵셔널일수 있음
- nil이 되므로 옵셔널
- 상호 독립적으로 존재하는 객체에 사용
class Person {
var apartment: Apartment?
deinit {
print("Person 객체 해제")
}
}
class Apartment {
weak var tenant: Person?
deinit {
print("Apartment 객체 해제")
}
}
var man: Person? = Person() // Person Reference Count +1 = 1
var home: Apartment? = Apartment() // Apartment Reference Count +1 = 1
man?.apartment = home // Apartment Reference Count +1 = 2
home?.tenant = man // Person Reference Count = 1
man = nil // Person 객체 해제 // Reference Count 0 -> Person 객체 해제
home = nil // Apartment 객체 해제 // Referece Count 1 -> man 객체 해제되면서, home.tenant 에서 참조 하고있던 Person 의 Reference Count 없어짐 -> 결과적으로 home의 Reference Count 0 -> 객체 해제
- unowned
- 옵셔널 타입으로 선언 불가(Initializer)를 생성 할수도 있음
- 완전히 종속적인 경우에 사용
- 참조하던 객체가 해제되어도 nil로 변하지 않음 -> Dangling pointer 위험
- 단독을 존재 못하는 종속적인 경우
class Country {
var capital : Capital!
deinit {
print("Country 객체 해제")
}
}
class Capital {
unowned var country: Country
init(country: Country) {
self.country = country
}
}
var korea: Country! = Country()
var seoul: Capital! = Capital(country: korea)
korea.capital = seoul
korea = nil // Country 객체 해제
Reference
WWDC Introducing Automatic Reference Counting
WWDC Adopting Automatic Reference Counting
About Memory Management
Memory Management Programming Guide for Core Foundation