Open hamfan524 opened 11 months ago
ARC- 자동 참조 계산 (Automatic Reference Counting)
는 앱의 메모리 사용량을 자동으로 관리하는 데 사용되는 메모리 관리 기술입니다.
ARC는 앱의 메모리 사용량을 추적하고 관리하여 클래스 인스턴스가 더 이상 필요하지 않을 때 할당이 취소되도록 합니다.
순환 참조
는 두 클래스 인스턴스가 서로에 대한 강력한 참조를 보유하여 강력한 참조 순환을 생성할 때 발생합니다.
순환 참조가 문제가 되는 이유는 위에서 설명한ARC
를 이용하여 참조 카운트가 0 이 되면 메모리에서 할당 해제를 하고 있는데,
강한 순환 참조가 생기게 되면 참조 카운트가 0 이 되지 않아 실제로 사용을 하지 않는 인스턴스가 메모리를 차지하고 있는 메모리 누수 현상이 발생하기 때문입니다.
Swift 에서 Reference Type 의 인스턴스를 참조하는 방식은 Strong, weak, unowned 3가지가 있습니다.
strong (강한 참조)
weak reference
unowned reference
여기서 weak
와 unowned
는 참조 카운트를 증가시키지 않아 순환 참조를 해결할 수 있는 방법이 될 수 있습니다.
두 방식의 차이가 있다면, weak
와 다르게 unowned
옵셔널 타입으로 선언하지 않아도 됩니다.
그 이유는 unowned
참조를 하면 참조하는 인스턴스가 메모리에서 할당 해제되어도 여전히 Heap 영역에 인스턴스가 있던 곳을 가리키고 있습니다. 즉, 참조하고 있던 주소만 가르키고 있는 허상 포인터가 됩니다.
그렇기 때문에, 참조하는 인스턴스가 먼저 메모리에서 해제될 가능성이 없는 경우에만 사용해야합니다.
weak
와 unowned
를 사용하여 순환 참조를 방지할 때 주의 사항
class Person {
var pet: Pet?
}
class Pet {
weak var owner: Person?
}
'Pet'은 'Person'에 대한 약한 참조를 갖습니다.
'Person' 인스턴스가 더 이상 참조되지 않고 ARC에 의해 할당 취소되면 해당 'Pet'인스턴스의 owner는 자동으로 nil이 됩니다.
더 이상 가리키는 유효한 'Person' 인스턴스가 없기 때문입니다.
Swift에서 캡처 목록은 클로저 외부의 값을 캡처하고 유지하는 방법에 대한 규칙을 정의하기 위해 클로저 내에서 사용됩니다.
Swift의 클로저는 정의된 컨텍스트에서 상수 또는 변수에 대한 참조를 캡처하고 저장할 수 있습니다. 이를 캡처 값이라고 합니다.
이는 강력한 기능이지만 특히 캡처된 변수가 클래스의 인스턴스인 경우 강력한 참조 순환으로 이어질 수 있습니다.
class MyClass {
var property: String = "Hello"
func doSomething() {
let closure = { [weak self] in
print(self?.property ?? "Default")
}
closure()
}
}
이 예시에서 'self'는 클로저 내에서 약하게 캡처됩니다.
MyClass 인스턴스가 할당 해제되면 클로저 내부의 self는 nil이 되어 강력한 참조 순환을 방지합니다.
class MyClass {
var property: String = "Hello"
func doSomething() {
let closure = { [unowned self] in
print(self.property)
}
closure()
}
}
여기서 'self'는 소유되지 않은 상태로 캡처됩니다.
클로저가 생명주기동안 항상 'self'를 참조한다는 것을 알 때 사용됩니다.
클로저에서도 주의사항
ARC가 앱의 메모리 사용량을 자동으로 관리하는 데 사용되는 메모리 관리 기술이고, 클래스 인스턴스가 더 이상 필요하지 않을 때 할당이 취소되도록 한다라는거 이해했습니다.
강한 참조, 약한 참조 말은 들어보긴 했는데 자세하게 적어주니 참고하는데 도움이 많이 되었습니다 그럼에도 강한 참조, 약한 참조를 어떤경우에 사용해야하는지 모르겠습니다 굳이 저걸 안써도 잘 돌아가는데 써야 할 이유를 아직 못느끼는게 문제인거 같아요 그래서 저걸 써야만 하는 상황을 설명해 주시면 좋을거 같습니다 ㅎㅎ
음 경고를 표시하는 뷰 컨트롤러로 예시를 들어보겠습니다. 경고하는 부분은 뷰 컨트롤러에 대한 참조가 필요하지 않는데 뷰컨트롤러를 참조하는 경우, 이 참조가 강력한 참조 순환이 되는 경우가 있을 수 있습니다.
약한 참조가 없는 예제 코드(문제 있는 경우)
class ViewController {
var alert: Alert?
func presentAlert() {
alert = Alert(viewController: self)
// 알림 보여주기
}
}
class Alert {
var viewController: ViewController
init(viewController: ViewController) {
self.viewController = viewController
}
func dismiss() {
}
}
예시에서 ViewController에는 Alert에 대한 강력한 참조가 있고 Alert에도 ViewController에 대한 강력한 참조가 있습니다. 이 설정은 강력한 참조 순환으로 이어져 실제로 더 이상 사용을 하지 않는 상황에서 ARC가 자동 할당 해제를 해야하지만, 두 개체 모두 할당이 해제되지 않을 수 있습니다.
약한 참조 사용 예제코드
class ViewController {
var alert: Alert?
func presentAlert() {
alert = Alert(viewController: self)
// 알림 보여주기
}
}
class Alert {
weak var viewController: ViewController?
init(viewController: ViewController) {
self.viewController = viewController
}
func dismiss() {
}
}
이런 변경으로 인해 강력한 참조 순환이 방지됩니다.
ViewController
는 Alert
를 생성하고 소유합니다.Alert
에는 ViewController
에 대한 약한 참조가 있어, ViewController
와 상호작용하거나 업데이트할 수 있지만 자체적으로 메모리에 보관하지는 않습니다.ViewController
가 해제되거나 할당이 취소될 때(예: 화면에서 다른 곳으로 이동할 때) Alert
은 ViewController
의 할당이 취소되는 것을 막지 않습니다.ViewController
할당이 해제되면 Alert
의 약한 참조는 nil이 되며 액세스를 시도할 때 충돌이 발생하지 않는 상태가 됩니다.
소규모 예제나 수명이 짧은 애플리케이션에서는 즉시 눈에 띄지 않지만, 크고 복잡한 애플리케이션, 특히 장기간 실행되거나 상호 연결된 많은 개체를 처리하는 앱에서는 메모리를 효과적으로 관리하는 것이 중요합니다. 효율적인 메모리 관리를 보장하고, 애플리케이션의 메모리 누수를 방지하려면 iOS 개발에서 약한 참조를 이해하고 올바르게 사용하는 것이 필수적입니다.
Swift에서의 메모리 관리 방법(ARC)에 대해 설명하고, 순환 참조(Circular Reference)를 방지하기 위한 전략은 무엇인가요?