Open Phangg opened 6 months ago
Swift 에서 메모리 관리를 자동으로 해주는 것으로 참조 카운트 (RC) 를 관리해주는 기술
참조 값이 사용되지 않을 때, RC를 자동으로 감소시켜주고 0이 되면 자동으로 매모리 해제 시킴
retain cycle ( 순환 참조 ) 을 주의해야 함
ARC 동작은 run time 이 아니라, compile time
에 실행 돰
과거 Objective-C 에서는 MRC 라고.. RC 를 수동으로 관리해주었음
메모리 누수 ( Memory Leak ) 가 발생 -> Weak, Unowned 을 사용하여 해결 동적으로 관리해준다고 했는데 run time 이 아닌, compile time 에서 어떻게 다 이루어지지? compile 시점에서 코드를 분석하여, retain 과 release 를 코드 내부에 적절한 위치에 삽입 이후 run time 에 해당 retain, release 가 실행되면서 RC 를 관리해줄 수 있음
retain : 참조 카운트를 증가 release : 참조 카운트를 감소
메모리가 해제되지 않고 메모리 누수 ( Memory Leak ) 가 생기는 현상. 참조가 순환되고 있는 상태.
class Person {
let name: String
var home: Home?
init(name: String) {
self.name = name
print("\(self.name) - Init")
}
deinit {
print("\(name) - Deinit")
}
}
class Home {
let homeType: String
var tenant: Person?
init(homeType: String) {
self.homeType = homeType
print("\(self.homeType) - Init")
}
deinit {
print("\(homeType) - Deinit")
}
}
// Person retain
var lee: Person? = Person(name: "Lee") // Lee - Init & rc = 1
// Home retain
var home: Home? = Home(homeType: "APT") // APT - Init & rc = 1
// Home retain
lee?.home = home // rc = 2
// Person retain
home?.tenant = lee // rc = 2
// Person release
lee = nil // rc = 1
// Home release
home = nil // rc = 1
// 두 인스턴스가 서로 강한 참조를 통해, 순환 참조가 이루어짐
// rc 가 0 이 될 수 없는 상태, memory leak
해결을 위해, Weak Reference 사용 ( default : Strong ) Unowned Reference 사용
class Person {
let name: String
var home: Home?
init(name: String) {
self.name = name
print("\(self.name) - Init")
}
deinit {
print("\(name) - Deinit")
}
}
class Home {
let homeType: String
weak var tenant: Person?
init(homeType: String) {
self.homeType = homeType
print("\(self.homeType) - Init")
}
deinit {
print("\(homeType) - Deinit")
}
}
// Person retain
var lee: Person? = Person(name: "Lee") // Lee - Init & rc = 1
// Home retain
var home: Home? = Home(homeType: "APT") // APT - Init & rc = 1
// retain X (weak)
lee?.home = home // rc = 1
home?.tenant = lee // rc = 1
// Person release
lee = nil // Lee - Deinit & rc = 0
// Home release
home = nil // APT - Deinit & rc = 0
weak 키워드는 한 쪽에만 붙여줘도 된다. 이때, 해당 프로퍼티의 타입이 항상 Optional 이어야 한다. ( 당연히, var ) -> 참조하는 인스턴스가 메모리에서 해제 되었을 경우, 자동으로 nil 을 할당하기 때문
class Owner {
let name: String
var company: Company?
init(name: String) {
self.name = name
print("\(self.name) - Init")
}
deinit {
print("\(name) - Deinit")
}
}
class Company {
let name: String
unowned let owner: Owner
init(name: String, owner: Owner) {
self.name = name
self.owner = owner
print("\(self.name) - Init")
}
deinit {
print("\(name) - Deinit")
}
}
var jun: Owner? = Owner(name: "jun") // jun - Init
jun?.company = Company(name: "xxx", owner: jun!) // xxx - Init
jun = nil // jun - Deinit & xxx - Deinit
jun 이 nil 이 될 때, 참조하고 있던 company 도 같이 사라짐
참조 카운트가 0이 되지 않았는데 원본이 사라지면 Crash 에러가 난다. 원본의 주소를 그대로 가리키고 있기 때문. 따라서, life cycle 을 고려해야 한다.
메모리 관리를 프로그램 실행중에 동적으로 관리해주는 기술
감시하고 있다가, 사용을 하지 않는 상황에 메모리를 삭제 해주는 것
( 어떤 변수도 가르키지 않게 된 메모리 )
run time
에 메모리가 관리 됨
( 메모리사용, CPU 점유가 생길 수 밖에 없음 )
ARC 에 비해, 인스턴스가 해제 될 가능성이 더 높음
( 메모리를 지속적으로 감시하고 있기 때문 )
어떤 메모리를 해제해야 할 지 결정할 때 사용되는 비용이 많이 들게 됨
( GC 의 알고리즘을 통해, 메모리 해제 시점을 찾아야하기 때문 )
또한, GC 가 실행되는 시간이나 타이밍을 제대로 알기 어려움
( 할당 해제 타이밍을 알 수 없음 )
GC 의 경우, 런타임에서 작동하게 되는데 이때, 메모리와 CPU 를 사용하게 됨 기기의 메모리와 CPU 가 제한적인 모바일 기기에서 사용하기에 성능이 더 좋은 ARC 를 사용하지 않을까 생각 함
순환참조가 아닐경우에는 강한 참조여도 괜찮은가요?
개발자의 실수에 의해 순환참조가 발생할 수 있는데, 이를 예방하기 위한 작업들에는 무엇이 있을까요?
순환참조가 아닐경우에는 강한 참조여도 괜찮은가요?
네! default 가 강한참조인데, 사이클이 생기지 않는다면 ARC 에서 자동으로 관리되기 때문에 그냥 두어도 괜찮습니다.
개발자의 실수에 의해 순환참조가 발생할 수 있는데, 이를 예방하기 위한 작업들에는 무엇이 있을까요?
제가 알기로는 실수를 할 수 있다는게 ARC 의 단점입니다.. 그래서 개발자는 직접 weak , unowned 를 잘써서 메모리를 관리해야합니다.
동호님의 이슈를 보며 알게 된 부분이지만,
Autorelease Pool
을 이용하는 것도 하나의 방법일 수 있다고 생각합니다.
개발자의 실수? 라기 보다는 메모리가 해제되지 않을 수 있는 많은 데이터를 요구하는 로직에서 사용됨이 더 맞을 것 같아요!
사실 상, Objective-C 에서 사용되던 레거시 코드에 가깝다고 하네요. 현재 Swfit 의 언어 특성상, 구조체를 많이 사용하고 ARC 를 활용하기에 직접 사용할 일이 많지 않지만 알아두는게 맞는 것 같습니다 ㅎㅎ
아래는 Autorelease Pool 에 대한 글을 모아봤으니 읽어봐도 좋을 것 같아요!
Autorelease Pool 란? - 간단한 설명 Autorelease Pool 성능 테스트 Autorelease Pool 의 역사와 성능 테스트 등 - 영문 Autorelease Pool 의 역사와 성능 테스트 등 - 위 블로그 해석 및 요약 블로그
iOS에서 자동 참조 카운팅(ARC)과 가비지 컬렉션(Garbage Collection)의 차이점에 대해 설명해주세요.