Weak Reference(약한 참조)는 ARC에서 객체의 보유 카운트를 증가시키지 않는 참조입니다. Apple Swift Language Guide, 2026에 따르면, 약한 참조는 weak 키워드로 선언되며 항상 옵셔널입니다. 객체가 해제되면 해당 객체에 대한 모든 약한 참조가 자동으로 nil로 설정되어 댕글링 포인터를 방지하고 약한 참조를 보유 주기를 끊는 안전한 메커니즘으로 만듭니다.
핵심 요점
weak 키워드; 타입은 항상 옵셔널 (?)Weak Reference는 ARC(Automatic Reference Counting)에서 객체에 대한 비소유 참조입니다. 객체의 retain count를 증가시키고 수명을 보장하는 강한 참조와 달리, 약한 참조는 객체가 여전히 참조되고 있더라도 해제될 수 있도록 허용합니다. 할당 해제 후 약한 참조는 자동으로 nil로 설정됩니다 — 이를 zeroing weak라고 합니다.
Zeroing weak는 Swift 및 Objective-C 런타임의 핵심 기능입니다. 객체의 참조 카운트가 0에 도달하고 객체가 할당 해제되면, 런타임은 이 객체에 대한 모든 약한 참조(특수 약한 테이블에 저장됨)를 순회하여 nil로 설정합니다. 이를 통해 약한 참조를 통해 해제된 메모리에 액세스(use-after-free)하는 것이 불가능해집니다 — 모든 읽기는 nil을 반환합니다.
Apple WWDC 2012 Session 406에 따르면, zeroing weak 참조는 수동 메모리 관리(MRR)에서 흔했던 댕글링 포인터 관련 충돌 버그의 전체 클래스를 제거했습니다. MRR에서 약한 참조는 __unsafe_unretained로만 존재했으며 — 0이 되지 않았고, 해제된 객체에 액세스하면 EXC_BAD_ACCESS가 발생했습니다.
Apple 생태계의 두 언어 모두에서 약한 참조를 선언하는 문법을 살펴보겠습니다. 런타임은 공유되지만 문법은 다릅니다. 그러나 의미는 동일합니다.
Swift에서 약한 참조는 var 앞에 weak 키워드로 선언됩니다. 참조는 언제든지 nil이 될 수 있으므로 타입은 항상 옵셔널(Type?)이어야 합니다. 상수(let)는 weak가 될 수 없습니다 — 변수만 가능합니다.
class ViewController: UIViewController {
// weak properties: only var, only optional
weak var delegate: ViewControllerDelegate?
weak var parentView: UIView?
weak var completionHandler: ((Bool) -> Void)? // ⚠️ 클로저는 weak를 저장하지 않음
// ⬆️ 오류: weak는 class 타입에만 적용 가능, 클로저에는 불가
}
중요: weak는 클래스 인스턴스(class 타입), AnyObject 및 AnyObject에서 상속된 프로토콜에만 적용 가능합니다. Struct, enum 및 클로저는 weak가 될 수 없습니다 — 값 타입이며 ARC에 참여하지 않습니다.
Objective-C에서 약한 속성은 __weak 속성 또는 속성 선언에서 weak 수정자를 사용하여 선언됩니다:
// Objective-C: weak property
@interface MyViewController : UIViewController
@property (weak, nonatomic) id<MyDelegate> delegate;
@end
// 지역 weak 변수
__weak MyObject *weakRef = someStrongObject;
Objective-C 런타임도 zeroing weak를 제공하지만, C 구조체 및 일부 Core Foundation 객체와의 weak 사용을 추가로 차단합니다. 이러한 경우 __unsafe_unretained가 사용됩니다 — zeroing 없이.
약한 참조는 보편적인 해결책이 아니라 특정 시나리오를 위한 도구입니다. 모든 곳에서 weak를 사용하면 불필요한 복잡성이 발생하고 가독성이 저하됩니다. 올바른 사용 시나리오를 살펴보겠습니다.
델리게이트 — weak의 주요 시나리오입니다. 소유 객체(예: UITableView)는 자신에 대한 강한 참조를 유지하는 반면, 델리게이트(UIViewController)는 테이블을 소유해서는 안 됩니다. Apple SDK는 모든 델리게이트와 dataSource가 weak임을 보장합니다. 자체 프로토콜의 경우 항상 weak var delegate를 사용하세요.
자식 객체가 부모를 참조해야 하는 경우(예: ChildViewController가 코디네이터에 액세스), 약한 참조를 사용하세요. 부모는 자식을 소유하고(strong), 자식은 부모를 관찰합니다(weak) — 보유 주기가 제거됩니다.
Capture list [weak self] — 클래스 속성으로 저장된 클로저에서 보유 주기를 방지하는 표준 방법입니다. 클로저가 완료되기 전에 self가 해제될 수 있는 경우 weak self는 필수입니다.
| 시나리오 | Weak | Strong |
|---|---|---|
| 델리게이트 | ✅ 항상 weak | ❌ 보유 주기 |
| 부모 → 자식 | ❌ 불필요(부모가 소유해야 함) | ✅ Strong |
| 자식 → 부모 | ✅ Weak | ❌ 보유 주기 |
| 비동기 콜백 | ✅ [weak self] | ❌ 보유 주기 위험 |
| 강한 결합(owned) | ❌ unowned | ✅ Strong |
일반 규칙: 객체 A가 B를 소유하는 경우(A → B strong), B → A는 weak 또는 unowned여야 합니다. 강한 참조의 방향은 항상 소유자에서 종속자로 향해야 합니다.
weak와 unowned 모두 retain count를 증가시키지 않지만, 객체 할당 해제 후 동작에서 차이가 있습니다. 둘 사이의 선택은 수명 보장의 문제입니다.
Weak: 자동으로 nil이 됩니다. 타입은 항상 옵셔널이며 사용 전 언래핑이 필요합니다. 안전 — nil 액세스는 충돌을 일으키지 않습니다.
Unowned: nil이 되지 않습니다. 타입은 비옵셔널입니다. 객체가 해제되면 unowned 참조는 댕글링 포인터가 됩니다 — 액세스하면 런타임 충돌이 발생합니다. Unowned는 객체가 참조 측보다 적어도 동일한 기간 동안 살아 있음을 가정합니다.
Weak를 선택하세요: 객체가 언제든지 해제될 수 있는 경우(화면 종료 후 델리게이트), 객체의 수명을 제어할 수 없는 경우, 또는 보장이 확실하지 않은 경우. Weak는 보편적인 안전한 선택입니다.
Unowned를 선택하세요: 객체가 참조 객체보다 먼저 해제되지 않음이 보장되는 경우(예: Customer → CreditCard, 카드는 고객 없이 존재하지 않음). Unowned는 언래핑 없이 비옵셔널 API를 제공하여 코드에서 더 편리합니다.
class Order {
let id: Int
var items: [Item] = []
init(id: Int) { self.id = id }
// 강한 관계: Order가 Item을 소유
func addItem(name: String) {
let item = Item(name: name, order: self)
items.append(item)
}
}
class Item {
let name: String
unowned let order: Order // ✅ unowned — Item은 Order 없이 생존 불가
init(name: String, order: Order) {
self.name = name
self.order = order
}
}
// weak 예제: 수명 보장 없는 델리게이트
protocol NetworkServiceDelegate: AnyObject {
func didReceiveResponse(data: Data)
}
class NetworkService {
weak var delegate: NetworkServiceDelegate? // ✅ weak — 델리게이트가 사라질 수 있음
}
예제에서 Item은 unowned를 사용합니다. 주문 항목은 주문 자체 없이 존재할 수 없기 때문입니다 — 수명 보장은 확실합니다. NetworkService는 weak를 사용합니다. 델리게이트(예: ViewController)가 언제든지 닫히고 해제될 수 있기 때문입니다.
약한 참조는 강력한 도구이지만, iOS 개발에서 올바르게 사용하기 위해 이해해야 할 제한 사항이 있습니다.
약한 참조는 강한 참조보다 느립니다: 각 액세스 시 런타임은 객체가 해제되었는지 확인합니다(weak 테이블에서 lookup). 대부분의 시나리오에서 차이는 미미하지만, 수백만 번의 액세스가 있는 핫 루프에서는 weak가 병목 현상이 될 수 있습니다. 고부하 시나리오의 경우 strong을 사용하고 아키텍처를 재구성하세요.
Struct, enum, tuple — ARC에 참여하지 않는 값 타입입니다. weak struct를 선언하려고 하면 컴파일 오류가 발생합니다. 값 타입에 대한 약한 참조를 저장하려면 클래스 타입의 래퍼 또는 클로저를 사용하세요.
Zeroing weak는 스레드 안전합니다: 한 스레드에서 객체가 해제되면 모든 스레드에서 약한 참조가 원자적으로 0이 됩니다. 그러나 약한 참조를 읽고 역참조하는 사이의 간격으로 인해 경합 상태가 발생할 수 있습니다 — 약한 참조를 획득하고 사용하는 사이에 객체가 해제됩니다. 해결책: 약한 참조를 지역 변수로 strong 캡처합니다.
// 멀티스레딩에서 weak의 경합 상태
func performAsync() {
weak var weakSelf = self
queue.async {
// ⚠️ weakSelf는 확인과 사용 사이에 nil이 될 수 있음
if weakSelf != nil {
weakSelf!.doSomething() // nil이 되면 CRASH
}
}
}
// ✅ 수정: 사용 중 strong 캡처
func performAsyncSafe() {
queue.async { [weak self] in
guard let strongSelf = self else { return }
strongSelf.doSomething() // strongSelf — 지역 strong 참조
}
}
안전한 버전에서는 weak self가 캡처된 다음 즉시 지역 strong 변수 strongSelf로 언래핑됩니다. self가 아직 살아 있으면 블록 기간 동안 살아 있게 됩니다. 그렇지 않으면 guard가 트리거되고 코드가 실행되지 않습니다. 이 관용구는 Swift에서 비동기 클로저의 표준 패턴입니다.
IBOutlet은 Interface Builder에서 weak여야 합니다. 뷰 계층이 이미 하위 뷰에 대한 강한 참조를 보유하고 있기 때문입니다. 컨트롤러에서 강한 참조를 복제해도 보유 주기가 생성되지 않지만 중복됩니다. 아울렛에 대한 약한 참조는 Apple의 권장 사항이지만, 많은 개발자가 코드 단순화를 위해 strong을 사용합니다.
자주 묻는 질문
아니요, weak는 기존 객체 또는 nil만 가리킬 수 있습니다. 새 객체를 만들 때 먼저 강한 참조를 얻은(초기화자를 통해) 후에만 약한 참조를 할당할 수 있습니다. 시작 시 weak nil은 정상 상태입니다.
Weak는 참조 타입(클래스)만 관리하는 ARC를 기반으로 합니다. 값 타입(struct, enum)은 할당 시 복사되며 retain count가 없습니다. 값 타입과의 약한 관계에는 클래스에서 weak 속성이 있는 래퍼 또는 클로저를 사용하세요.
약한 참조에 대한 각 액세스는 런타임 테이블에서 lookup을 수행합니다. 수백만 번의 반복이 있는 루프에서는 강한 참조보다 2~5배 느릴 수 있습니다. 핫 경로의 경우 루프 전에 weak를 지역 strong 변수로 복사하세요.
객체에 대한 모든 강한 참조가 손실될 때 — 범위 끝, 속성 재할당 시, 또는 화면이 닫힐 때입니다. 멀티스레드 환경에서는 코드 두 줄 사이에 발생할 수 있습니다. 항상 guard let 또는 if let으로 약한 참조를 확인하세요.
의미적으로 동일합니다: 둘 다 zeroing weak를 제공합니다. 차이점: Swift는 옵셔널 타입과 var를 필요로 하고, Objective-C는 속성 수정자를 사용합니다. Objective-C는 __unsafe_unretained도 지원합니다 — zeroing 없는 약한 참조(댕글링 포인터 위험).
요약
weak var + 옵셔널 타입; class 타입 및 AnyObject 프로토콜만턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.