ONE QUESTION AT A TIME.

천천히 생각하고, 분명하게.

TODAY’S QUESTION730 / 1500
모바일난이도 하5분 답변 목표 · 보강본

ARC를 사용하는 Swift 객체가 해제되지 않습니다. 강한 순환 참조와 `weak`, `unowned`의 차이를 어떻게 진단하고 고치나요?

잠깐, 답변을 보기 전에
먼저 내 말로 설명해 볼까요?

답변과 점검 포인트 펼치기답변 접기
01

구두 답변

정의를 외우기보다, 원리와 조건을 연결해 설명해 보세요.

핵심은 객체를 무조건 weak로 바꾸는 것이 아니라, 실제 소유 관계와 사용 시점의 수명을 그래프로 그린 뒤 역방향 참조만 비소유로 만드는 것입니다. ARC는 강한 참조의 수를 관리해 강한 참조가 하나도 없어질 때 객체를 해제하지만, 두 객체가 서로를 강하게 참조하거나 객체가 자신을 탈출 클로저에 강하게 캡처하면 외부에서 화면을 닫아도 참조 수가 0이 되지 않습니다. 먼저 화면 전환 뒤 기대한 뷰 컨트롤러의 deinit이 호출되는지 확인하고, 메모리 그래프에서 서비스, 작업 핸들, 클로저가 어떤 방향으로 객체를 붙잡는지 찾겠습니다. weak를 붙이는 것만으로 원인을 가리면 필요한 작업 취소나 정리까지 사라질 수 있습니다.

소유 관계와 비소유 관계를 나눕니다

부모가 자식을 생성하고 자식의 생존을 책임지는 관계라면 부모의 자식 참조는 강해야 합니다. 반대로 자식이 부모를 다시 가리키는 역방향 링크, delegate, 관찰자처럼 대상이 먼저 사라질 수 있는 관계는 소유하지 않는 참조로 표현합니다. 대상이 먼저 해제되면 참조가 자동으로 nil이 되고, 콜백은 아무 일도 하지 않거나 필요한 취소·정리를 수행하게 만드는 방식이 약한 참조(weak)입니다. 예를 들어 뷰 컨트롤러가 오래 사는 다운로드 서비스에 등록된다면 서비스가 화면을 붙잡지 않도록 delegate를 weak로 두거나 클로저에서 [weak self]를 사용합니다. 다만 self가 이미 없어도 반드시 처리해야 하는 완료 기록은 화면에 맡기지 말고 별도의 작업 객체나 저장소가 소유해야 합니다.

unowned도 참조 수를 늘리지 않지만 자동으로 nil이 되지 않습니다. 참조 대상이 참조를 사용하는 전체 기간 동안 반드시 살아 있다는 계약이 깨지면 nil을 얻는 대신 런타임 오류가 납니다. 따라서 두 객체의 수명이 엄격히 묶이고 비동기 지연이나 캐시가 그 계약을 깨지 않는 경우에만 선택하겠습니다. 특히 [unowned self]를 비동기 클로저에 쓰는 것은 강한 순환을 없애는 간단한 문법이 아니라, 그 클로저가 참조를 사용하는 동안 self가 반드시 살아 있다는 보장이 있을 때만 안전합니다. 작업 큐, 타이머, 알림 구독처럼 실행 시점을 외부가 결정하는 곳에서는 별도의 수명 보장이 없다면 weak를 우선하겠습니다.

클로저 수명까지 확인합니다

작업 객체가 완료 후 클로저를 지우는지, 취소 시 지우는지, 여러 번 호출할 수 있는지 계약을 정해야 합니다. 서비스가 클로저를 보관하고 뷰 컨트롤러도 서비스를 보관하면 서비스 → 클로저 → 화면 → 서비스의 고리가 생길 수 있습니다. [weak self]로 고리를 끊으면 화면 해제는 가능하지만, 화면이 사라진 뒤 콜백의 결과를 누가 소비하는지 정하지 않으면 진행 상태나 임시 파일 정리가 누락됩니다. 한 번만 실행되는 작업은 성공·실패·취소가 어느 경로에서도 한 번만 완료되도록 원자적인 상태를 두고, 화면 생명주기에 묶인 작업은 viewWillDisappear나 명시적 소유자 해제 시점에 취소하겠습니다.

재현은 화면을 열고 닫는 동작을 반복하면서 deinit 로그와 메모리 그래프를 함께 확인하고, 콜백이 화면 해제 전후에 도착하는 경우를 나누어 시험합니다. weak 참조가 nil이 된 것이 정상인지, 아니면 서비스가 계속 살아야 하는 작업이 잘못 취소된 것인지도 확인해야 합니다. 결론적으로 강한 참조는 소유권을 표현하고, weak는 대상 부재를 정상 상태로 처리할 때, unowned는 대상이 사용 기간 내내 살아 있다는 수명 불변식이 확실할 때만 사용하겠습니다.

02

득점 포인트

이해가 드러나는 지점을 확인해 보세요.

  • ARC가 강한 참조 순환을 자동으로 끊지 못하는 이유와 메모리 그래프·deinit을 이용한 진단 순서를 설명한다.
  • weak를 대상이 먼저 사라질 수 있는 비소유 관계에 선택하고, nil 이후 필요한 작업 소유권을 별도로 둔다.
  • unowned가 자동 nil이 아니라 수명 계약 위반 시 런타임 오류를 내므로 비동기 지연에서는 신중해야 함을 구분한다.
03

감점 포인트

놓치기 쉬운 조건과 오해를 짚어 보세요.

  • 화면을 닫거나 변수를 nil로 만들면 모든 순환 참조가 ARC에 의해 해결된다고 단정한다.
  • 강한 순환을 없애기 위해 모든 self를 weak 또는 unowned로 바꾸고 완료 처리의 소유자를 잃는다.
  • unowned도 대상 해제 시 nil이 된다고 말하거나, 비동기 클로저의 실행 시점을 수명 보장 없이 안전하다고 본다.
04

더 파고들 거리

하나 더 질문하며 이해의 범위를 넓혀 보세요.

득점·감점 포인트는 학습용 자기 점검 기준이며, 특정 회사의 실제 채점표가 아닙니다.

원리부터 공부하고 싶다면

답변을 보충하는 개념·예제·슈도코드를 읽어 보세요.

← 전체 질문 보기