iOS :: ARC, strong, weak, unowned
안녕하세요!
상어입니다.
정말 오랜만에 글을 쓰는 것 같네요 ㅎㅎ
자주 써야지 써야지 생각하다가도 이래저래 바빠서 제대로 못 들어오는 것 같아요 ㅜ
꾸준히 하시는 분들 정말 리스펙 합니다,,
저는 오늘 ARC에 대해 적어볼까 해요.
ARC에 대해 상세히 설명하자면 엄청엄청 길고 할 말이 많지만, 이번 설명에선 간략하게 해보려 합니다 ㅎㅎ
ARC(Automatic Reference Counting)
모두 한 번쯤은 받아보신 질문일 거에요
"ARC가 뭔가요?"
저도 많이 받은 질문인데, ARC를 잘 모르는 시절엔 이렇게 답했습니다.
"자동으로 Retain Count를 올려주는 겁니다."
뭐... 틀린 말은 아니에요. 자동으로 Retain Count(RC)를 올려주는 걸 ARC라고 해요. 하지만! 너무너무너무 부족한 설명이죠. 좋은 답변이 아니었습니다 ㅠㅠ
그러면, 지금은 뭐라고 답을 하냐구요?
ARC는
- 컴파일 시 코드를 분석해서 자동으로 retain, release 코드를 생성해주는 것- 참조된 횟수를 추적해 더 이상 참조되지 않는 인스턴스를 메모리에서 해제해주는 것입니다.
라고 답을 합니다.
네,, ARC는 자동으로 RC를 관리해주기 때문에 메모리 해제에 대한 개발자의 부담을 덜어주는 것입니다.지금 이 모든 것을 설명하려면 너무너무 길기 때문에 자세한 설명은 넘어가겠습니다.
그럼 메모리를 참조하는 방법으로는 어떤 것이 있을까요?
strong
- 강한 참조- 해당 인스턴스의 소유권을 가짐- 자신이 참조하는 인스턴스의 retain count를 증가시킴
strong은 선언할 때 아무것도 적어주지 않으면 default로 strong이 됩니다.
var test = Test() // retain count 1 증가 test = nil // retain count가 1 감소되어 0이 되면서 메모리 해제됨
weak
- 약한 참조
- 해당 인스턴스의 소유권을 가지지 않음
- 자신이 참조하는 인스턴스의 retain count를 증가시키지 않음
weak var test = Test() // 객체가 생성 되지만 weak이기 때문에 바로 객체가 해제되어 nil이 됨
unowned
- 약한 참조
- 해당 인스턴스의 소유권을 가지지 않음
- 자신이 참조하는 인스턴스의 retain count를 증가시키지 않음
unowned var test = Test() // 객체 생성과 동시에 해제되고 댕글포인트만 가지고 있음. 에러남
위의 설명을 보시면 weak와 unowned 설명이 동일합니다. 그럼 같은거냐?! 그건 아닙니다.
weak와 unowned의 차이로 흔히들 nil이 가능한지 아닌지의 여부라고 합니다. 이것을 좀 더 자세히 설명하자면,
weak는 객체를 계속 추적하면서 객체가 사라지게 되면 nil로 바꿉니다.
하지만, unowned는 객체가 사라지면 댕글포인터가 남습니다. 그래서 이 댕글포인터를 참조하게 되면 crash가 나는데 이 때문에 unowned는 사라지지 않을 거라고 보장되는 객체에만 설정하는 것이 crash가 발생하는 것을 방지하는 것에 도움이 됩니다.
* 댕글포인터는 원래 바라보던 객체가 해제되면서 할당되지 않는 공간을 바라보는 포인터입니다.*
그럼 여기서 질문!
"안전성을 위해 unowned 대신 weak만 사용하면 되지 않을까요?"
네 맞습니다. 안전하게 가려면 unowned 대신 weak만 사용해도 됩니다.
하지만, unowned를 사용하는 이유는
weak는 객체를 계속 추적하고 있다고 했죠? 이렇게 계속 객체를 추적하고 있는 것도 런타임시 오버헤드라고 생각해서 확실한 객체에는 unowned를 사용하는 것입니다.
순환참조
ARC가 편하게 메모리를 관리해주지만 자칫 잘못하면 순환참조가 발생할 수 있습니다.
순환참조란,
서로가 서로를 소유하고 있어 절대 메모리 해제가 되지 않는다는 것을 말합니다.
어떤 경우가 있는지 살펴볼까요?
https://shark-sea.kr/entry/swift-delegate패턴-알아보기
일전에 delegate 패턴 설명을 기억하시나요?
delegate를 하기 위해서는 일을 시키는 객체와 일을 하는 객체 두 개가 무조건 있어야 하는데,
vc.delegate = self
위의 코드로 객체를 연결시켜줌으로 인해 FirstViewController와 SecondViewController는 서로를 소유하는 상황이 됩니다.
즉, 다시 말해 FirstViewController에서 SecondViewController 객체를 만듦으로 SecondViewController를 소유하고,
SecondViewController의 delegate를 FirstViewController로 연결해줌으로써 FirstViewController를 소유하는 순환참조가 됩니다.
이 문제를 해결하기 위해서는 SecondViewController의 delegate에 weak를 붙이면 되는데 그렇게 되면
위의 그림과 같이 FirstViewController만 SecondViewController를 소유하기 때문에 순환참조가 발생하지 않게 됩니다.
이상, ARC를 정말 간단하게 설명해봤는데 어떠셨나요 ㅎㅎ
이 내용은 ARC의 극히 일부분이기 때문에 다른 곳에서 꼭 원리도 같이 이해하셨으면 좋겠습니다!
안뇽!