Notice
Recent Posts
Recent Comments
Link
«   2025/04   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30
Archives
Today
Total
관리 메뉴

J

[Swift] weak, unowned 본문

Swift

[Swift] weak, unowned

yujaehui 2024. 4. 18. 00:36

1. weak와 unowned란?

Swift에서는 메모리 관리를 위해 강한 참조(Strong Reference), 약한 참조(Weak Reference), 비소유 참조(Unowned Reference)를 제공.

weak와 unowned는 참조 타입 사이에서 강한 순환 참조(Strong Reference Cycle)를 방지하는 데 사용.

둘 다 ARC(Automatic Reference Counting)의 메모리 누수 문제를 해결하는 데 중요한 역할.


2. weak와 unowned의 공통점

  • 강한 참조(Strong Reference)를 생성하지 않음.
  • 참조하는 인스턴스가 메모리에서 해제될 수 있도록 함.
  • 강한 순환 참조를 방지하여 메모리 누수를 줄임.

3. weak와 unowned의 차이점

  weak unowned
참조 대상 Optional로 선언됨 Non-Optional로 선언됨
메모리 해제 시 동작 참조 대상이 해제되면 nil로 설정 참조 대상이 해제되면 잘못된 참조 발생 가능
사용 상황 참조 대상이 해제될 수 있고, nil이 허용될 때 참조 대상이 해제되지 않거나, 반드시 존재할 때
메모리 해제 여부 확인 필요 필요 (if let이나 guard let 사용) 불필요

4. weak의 특징

  • 약한 참조로, 참조 대상이 메모리에서 해제되면 자동으로 nil로 설정.
  • 항상 옵셔널 타입이어야 하며, nil 가능성을 처리해야 함.
  • 주로 delegate 패턴에서 사용.
class Person {
    var name: String
    weak var pet: Pet? // weak 참조

    init(name: String) {
        self.name = name
    }

    deinit {
        print("\\(name) is being deinitialized.")
    }
}

class Pet {
    var name: String
    var owner: Person? // 강한 참조

    init(name: String) {
        self.name = name
    }

    deinit {
        print("\\(name) is being deinitialized.")
    }
}

var john: Person? = Person(name: "John")
var fluffy: Pet? = Pet(name: "Fluffy")

john?.pet = fluffy // Person이 Pet을 weak 참조
fluffy?.owner = john // Pet이 Person을 강한 참조

john = nil // Person 해제
// "John is being deinitialized."
fluffy = nil // Pet 해제
// "Fluffy is being deinitialized."
  • john?.pet은 weak 참조이므로, john이 해제되면 자동으로 nil로 설정.

5. unowned의 특징

  • 비소유 참조로, 참조 대상이 메모리에서 해제되더라도 nil로 설정되지 않음.
  • 옵셔널이 아닌 타입으로 선언.
  • 참조 대상이 반드시 존재한다고 가정할 때 주로 사용.
  • 참조 대상이 해제되면 런타임 오류가 발생.
class Customer {
    var name: String
    var card: CreditCard?

    init(name: String) {
        self.name = name
    }

    deinit {
        print("\\(name) is being deinitialized.")
    }
}

class CreditCard {
    var number: String
    unowned let customer: Customer // unowned 참조

    init(number: String, customer: Customer) {
        self.number = number
        self.customer = customer
    }

    deinit {
        print("Card \\(number) is being deinitialized.")
    }
}

var john: Customer? = Customer(name: "John")
john?.card = CreditCard(number: "1234-5678-9012", customer: john!)

john = nil
// "John is being deinitialized."
// "Card 1234-5678-9012 is being deinitialized."
  • CreditCard는 unowned 참조를 통해 Customer를 참조하며, john 해제 시 CreditCard도 정상적으로 해제.

6. weak와 unowned 사용 시 주의 사항

weak는 항상 옵셔널

  • weak는 참조 대상이 해제되면 자동으로 nil로 설정되므로, 반드시 옵셔널 타입.
  • 사용 시 if let이나 guard let로 안전하게 처리해야 함.
if let pet = person.pet {
    print("\\(pet.name) is owned by \\(person.name).")
}

unowned는 옵셔널이 아님

  • unowned는 참조 대상이 해제될 가능성이 없는 경우에만 사용해야 함.
  • 참조 대상이 해제되면 런타임 오류가 발생하므로, 안전성에 주의.

강한 순환 참조 방지

  • weak와 unowned는 강한 순환 참조를 방지하기 위해 설계.
  • 참조 그래프를 설계할 때, 서로 참조하는 객체 간의 관계를 명확히 정의해야 함.

7. 언제 weak와 unowned를 사용할까?

상황 weak 사용 unowned 사용
참조 대상이 해제될 가능성이 있음
참조 대상이 반드시 존재한다고 가정됨
옵셔널 값 허용
delegate 패턴
순환 참조를 방지하고 안전성 필요

8. weak와 unowned의 실질적 사용 예

Delegate 패턴에서 weak

weak는 delegate 패턴에서 흔히 사용.

protocol TaskDelegate: AnyObject {
    func taskDidComplete()
}

class Task {
    weak var delegate: TaskDelegate?

    func completeTask() {
        delegate?.taskDidComplete()
    }
}

class Manager: TaskDelegate {
    func taskDidComplete() {
        print("Task completed.")
    }
}

let task = Task()
let manager = Manager()

task.delegate = manager
task.completeTask() // "Task completed."

부모-자식 관계에서 unowned

unowned는 부모-자식 관계에서 자식이 부모를 참조해야 할 때 사용.

class Parent {
    var child: Child?

    deinit {
        print("Parent is being deinitialized.")
    }
}

class Child {
    unowned let parent: Parent

    init(parent: Parent) {
        self.parent = parent
    }

    deinit {
        print("Child is being deinitialized.")
    }
}

var parent: Parent? = Parent()
parent?.child = Child(parent: parent!)

parent = nil
// "Child is being deinitialized."
// "Parent is being deinitialized."

9. 결론

  • weak: 참조 대상이 해제될 가능성이 있을 때 사용. 자동으로 nil로 설정되므로 안전함.
  • unowned: 참조 대상이 반드시 존재한다고 가정할 때 사용. 메모리 해제 후 참조 시 런타임 오류 발생 가능.
  • 강한 순환 참조 방지를 위해, 객체 간의 관계를 이해하고 weak와 unowned를 적절히 선택하는 것이 중요.
  • 일반적으로 weak는 더 안전하지만, 성능이 중요하고 메모리 안전성이 보장되는 경우 unowned를 사용.

'Swift' 카테고리의 다른 글

[Swift] map  (0) 2024.04.19
[Swift] init  (0) 2024.04.18
[Swift] self / Self  (0) 2024.04.18
[Swift] associatedtype  (0) 2024.04.18
[Swift] Protocol  (0) 2024.04.18