개발/swift

[swift] 참조

iris3455 2025. 4. 28. 00:11

strong (강한 참조)

  • Swift에서 객체를 참조할 때 기본으로 강한참조를 한다.
  • 누군가 강하게 참조하고 있는 한, 메모리에서 절대 해제되지 않습니다.
  • 참조하는 인스턴스의 Reference Count 를 증가시킨다.

 

weak (약한 참조)

  • 참조하는 인스턴스의 Reference Count 를 증가시키지 않는다.
  • 참조하는 인스턴스가 메모리에서 할당 해제될 경우 nil 이 할당된다.
  • self는 "옵셔널 타입"(self?)로 사용
    (약한 참조는 언제든 메모리에서 사라질 수 있기때문 -> nil이 될 수 있기 때문)

 

unowned (무소유 참조)

  • 참조하는 인스턴스의 Reference Count 를 증가시키지 않는다.
  • 메모리에서 할당 해제 되어도 nil이 되지 않는다.
    (참조하는 인스턴스가 메모리에서 할당 해제되어도 여전히 Heap 영역에 인스턴스가 있던 곳을 가리키고 있음)
  • 할당 해제되지 않은 인스턴스를 참조한다고 확신하는 경우에만 무소유 참조를 사용해야한다.
 

순환참조

  • Swift는 ARC (Automatic Reference Counting) 라는 방식으로 메모리 관리를 하고 있어 자동으로 메모리를 할당하고 해제
  • 참조 카운트(reference count)가 0이 되면, 인스턴스는 메모리에서 해제되어야 되는데 만약 A 객체가 B객체를 강하게 참조 하고 있고 B객체가 A객체를 강하게 참조하면 서로에 대한 참조가 해제되지 않기 때문에 둘다 메모리에서 해제되지 않아 순환참조 발생

 

예시코드

  • MailHandler → UIAlertController → UIAlertAction → 클로저 → MailHandler ...
  • 내가 만든 UIAlertController 안의 버튼(UIAlertAction)이 나를 참조
  • MailHandler 인스턴스가 생성시 (rc + 1) & UIAlertAction 클로저안에 self (MailHandler)를 사용 (rc + 1)
    -> mailHandler = nil 이 되어도 참조 카운트는 1이 남음 & deinit을 호출하지 않음 -> 메모리 누수 발생
class MailHandler: ObservableObject {
  @Published var showDefaultMailView = false
  
  private func showMailErrorAlert() {
  ...
  }
  
  func showMailOptions() {
    let alertController = UIAlertController(
      title: "메일 보내기",
      message: "메일을 보낼 앱을 선택해주세요",
      preferredStyle: .actionSheet
    )
        
    alertController.addAction(UIAlertAction(title: "Mail 앱", style: .default) { _ in
      if MFMailComposeViewController.canSendMail() {
        self.showDefaultMailView = true
      } else {
        self.showMailErrorAlert()
      }
    })
  }
}

 

 

해결 방법

 

1. week 사용

  • 클로저가 MailHandler를 강하게 잡지 않음
  • 참조하는 인스턴스의 Reference Count 를 증가시키지 않음
  • mailHandler를 nil로 하면 참조 카운트가 0이 되고 메모리 할당 해제
alertController.addAction(UIAlertAction(title: "Mail 앱", style: .default) { [weak self] _ in
  if MFMailComposeViewController.canSendMail() {
    self?.showDefaultMailView = true
  } else {
    self?.showMailErrorAlert()
  }
})

 

2. unknown 사용

  • MailHandler가 절대 메모리 해제되지 않는 상황 에서만 사용
alertController.addAction(UIAlertAction(title: "Mail 앱", style: .default) { [unowned self] _ in
  if MFMailComposeViewController.canSendMail() {
    self.showDefaultMailView = true
  } else {
    self.showMailErrorAlert()
  }
})