Skip to content

Latest commit

 

History

History
186 lines (170 loc) · 14.2 KB

7. ARC (2).md

File metadata and controls

186 lines (170 loc) · 14.2 KB

날짜: 2023-01-29 18:37

주제: strong, weak, unowned


메모:

Resolving Strong Reference Cycles Between Class Instances

  • Swift는 클래스 타입의 프로퍼티와 작업할 때 강한 참조 사이클을 해결하기 위해 2가지 방법을 제공한다.
    • weak references
    • unowned references
  • 약한 참조와 미소유 참조를 사용하면 참조 사이클의 한 인스턴스가 강한 유지 없이 다른 인스턴스를 참조할 수 있다.
  • 다른 인스턴스의 수명이 더 짧은 경우 즉, 다른 인스턴스가 먼저 할당 해제될 수 있을 때 약한 참조를 사용한다. 1의 예제에서 아파트는 어느 시점에 소유자가 없을 수 있는 것이 적절하므로 이러한 경우 약한 참조는 참조 사이클을 끊는 적절한 방법이다. 반대로 다른 인스턴스의 수명이 동일하거나 더 긴 경우 미소유 참조를 사용한다.
Weak References
  • 약한 참조 (weak reference) 는 참조하는 인스턴스를 강하게 유지하지 않는 참조이므로 ARC가 참조된 인스턴스를 처리하는 것을 중지하지 않습니다. 이러한 동작은 참조가 강한 참조 사이클의 일부가 되는 것을 방지합니다. 프로퍼티 또는 변수 선언 전에 weak 키워드를 위치시켜 약한 참조를 나타냅니다.
  • 약한 참조는 참조하는 인스턴스를 강하게 유지하지 않기 때문에 약한 참조가 참조하는 동안 해당 인스턴스가 할당 해제될 수 있습니다. 따라서 ARC는 참조하는 인스턴스가 할당 해제되면 nil 로 약한 참조를 자동으로 설정합니다. 그리고 약한 참조는 런타임에 값을 nil 로 변경하는 것을 허락해야 하므로 항상 옵셔널 타입의 상수가 아닌 변수로 선언됩니다.
class Person {
    let name: String
    init(name: String) { self.name = name }
    var apartment: Apartment?
    deinit { print("\(name) is being deinitialized") }
}

class Apartment {
    let unit: String
    init(unit: String) { self.unit = unit }
    weak var tenant: Person?
    deinit { print("Apartment \(unit) is being deinitialized") }
}

var john: Person?
var unit4A: Apartment?

john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")

john!.apartment = unit4A
unit4A!.tenant = john

![[스크린샷 2023-01-30 14.04.18.png|500]]

  • Person 인스턴스는 Apartment 인스턴스에 대해 아직 강한 참조를 가지고 있지만 Apartment 인스턴스는 이제 Person 인스턴스에 대해 약한 참조를 가지고 있습니다. 이것은 john 변수에 nil 을 설정하여 강한 참조를 끊으면 Person 인스턴스에 대해 더이상 강한 참조가 아님을 의미한다.
john = nil
// Prints "John Appleseed is being deinitialized"

![[스크린샷 2023-01-30 14.09.59.png|500]]

  • Apartment 인스턴스에 대한 유일한 강한 참조는 unit4A 변수에서 가져온 것입니다. 강한 참조를 끊으면 Apartment 인스턴스에 대한 강한 참조는 더이상 없다.
unit4A = nil
// Prints "Apartment 4A is being deinitialized"

![[스크린샷 2023-01-30 14.10.52.png|500]]

  • In systems that use garbage collection, weak pointers are sometimes used to implement a simple caching mechanism because objects with no strong references are deallocated only when memory pressure triggers garbage collection. However, with ARC, values are deallocated as soon as their last strong reference is removed, making weak references unsuitable for such a purpose.
Unowned References
  • 약한 참조와 마찬가지로 미소유 참조 (unowned reference) 는 참조하는 인스턴스를 강하게 유지하지 않습니다. 그러나 약한 참조와 다르게 미소유 참조는 다른 인스턴스의 수명이 같거나 더 긴 경우에 사용됩니다. 프로퍼티 또는 변수 선언 전에 unowned 키워드를 위치시켜 미소유 참조를 나타냅니다.
  • 약한 참조와 달리 미소유 참조는 항상 값을 갖도록 예상됩니다. 결과적으로 미소유로 만들어진 값은 옵셔널로 만들어 지지 않고 ARC는 미소유 참조의 값을 nil 로 설정하지 않습니다.
  • 참조가 항상 할당 해제되지 않은 인스턴스를 참조한다고 확신하는 경우에만 미소유 참조를 사용해야 한다. 인스턴스가 할당 해제된 후에 미소우 참조의 값에 접근하려고 하면 런타임 에러가 발생한다.
  • Customer 와 CreditCard 간의 관계는 위의 약한 참조 예제에서 본 Apartment 와 Person 간의 관계와 약간 다르다. 이 데이터 모델은 고객은 신용카드를 가지고 있거나 가지고 있지 않을 수 있지만 신용카드는 항상 고객과 연관되어 있다. CreditCard 인스턴스는 참조하는 Customer 보다 오래 지속되지 않습니다. 이것을 표현하기 위해 Customer 클래스는 옵셔널 card 프로퍼티를 가지지만 CreditCard 클래스는 미소유와 옵셔널이 아닌 customer프로퍼티를 가집니다.
  • 또한 새로운 CreditCard 인스턴스는 사용자 정의 CreditCard 초기화 구문에 number 값과 customer 인스턴스를 전달해서만 생성될 수 있습니다. 이렇게 하면 CreditCard 인스턴스가 생성될 때 CreditCard 인스턴스에 항상 연관된 customer 인스턴스를 가지고 있습니다.
  • 신용카드는 항상 고객을 가지고 있으므로 강한 참조 사이클을 피하기 위해 customer 프로퍼티에 미소유 참조로 정의합니다
class Customer {
    let name: String
    var card: CreditCard?
    init(name: String) {
        self.name = name
    }
    deinit { print("\(name) is being deinitialized") }
}

class CreditCard {
    let number: UInt64
    unowned let customer: Customer
    init(number: UInt64, customer: Customer) {
        self.number = number
        self.customer = customer
    }
    deinit { print("Card #\(number) is being deinitialized") }
}

var john: Customer?

john = Customer(name: "John Appleseed")
john!.card = CreditCard(number: 1234_5678_9012_3456, customer: john!)

![[스크린샷 2023-01-30 14.23.36.png|500]]

  • Customer 인스턴스는 이제 CreditCard 인스턴스에 대한 강한 참조를 가지고 있고 CreditCard 인스턴스는 Customer 인스턴스에 대해 미소유 참조를 가지고 있습니다.
  • john 변수에 의해 강한 참조를 끊을 때 미소유 customer 참조 때문에 Customer 인스턴스에 대해 더이상 강한 참조를 가지지 않습니다 ![[스크린샷 2023-01-30 14.29.49.png|500]]
  • 더이상 Customer 인스턴스에 대해 강한 참조가 아니므로 할당 해제됩니다. 후에 CreditCard 인스턴스에 대해 더이상 강한 참조가 아니므로 이것도 할당 해제됩니다:
john = nil
// Prints "John Appleseed is being deinitialized"
// Prints "Card #1234567890123456 is being deinitialized"
  • 위의 마지막 코드는 john 변수를 nil 로 설정한 후에 Customer 인스턴스와 CreditCard 인스턴스 모두 "deinitialized" 메서지를 출력하는 초기화 해제 구문을 보여줍니다. ![[Pasted image 20230130150508.png]]
  • unowned, weak reference에 대한 나의 이해
Unowned Optional References
  • 클래스에 옵셔널 참조를 미소유로 표기할 수 있습니다. ARC 소유권 모델 측면에서 미소유 옵셔널 참조 (unowned optional reference)와 약한 참조는 모두 같은 컨텍스트에서 사용될 수 있습니다. 차이점은 미소유 옵셔널 참조를 사용할 때 유효한 객체를 참조하거나 nil 로 설정되어 있는지 확인해야 합니다.
class Department {
    var name: String
    var courses: [Course]
    init(name: String) {
        self.name = name
        self.courses = []
    }
}

class Course {
    var name: String
    unowned var department: Department
    unowned var nextCourse: Course?
    init(name: String, in department: Department) {
        self.name = name
        self.department = department
        self.nextCourse = nil
    }
}
  • Department 는 과에서 제공하는 각 과정에 강한 참조를 유지합니다. ARC 소유권 모델에서 과는 과정을 소유하고 있습니다. Course 는 과에 대한 것과 객체를 소유하지 않은 학생이 수강해야 하는 다음 과정에 대한 2개의 미소유 참조를 가지고 있습니다. 모든 과정은 과의 부분이므로 department 프로퍼티는 옵셔널이 아닙니다. 그러나 일부 과정은 후속 과정이 없기 때문에 nextCourse 프로퍼티는 옵셔널 입니다.
let department = Department(name: "Horticulture")

let intro = Course(name: "Survey of Plants", in: department)
let intermediate = Course(name: "Growing Common Herbs", in: department)
let advanced = Course(name: "Caring for Tropical Plants", in: department)

intro.nextCourse = intermediate
intermediate.nextCourse = advanced
department.courses = [intro, intermediate, advanced]
  • 위의 코드는 과와 그 과의 3개의 과정을 생성합니다. 소개와 중급 과정 모두 nextCourse 프로퍼티에 저장된 다음 과정을 제안하며 이는 학생이 과정을 완료한 후 수강해야 하는 과정에 대한 미소유 옵셔널 참조를 유지합니다.

  • 미소유 옵셔널 참조는 래핑하는 클래스의 인스턴스에 강하게 유지하지 않으므로 ARC가 인스턴스를 할당 해제하는 것을 방지하지 않습니다. 미소유 옵셔널 참조가 nil 이 될 수 있다는 점을 제외하고 미소유 참조는 ARC에서 수행하는 것과 동일하게 동작합니다.

Unowned References and Implicitly Unwrapped Optional Properties
  • 위에서 약한 참조와 미소유 참조에 대한 예제는 강한 참조 사이클을 중단해야 하는 일반적인 2가지 시나리오를 다룹니다.
  • Person 과 Apartment 예제는 둘 다 nil 이 될 수 있는 프로퍼티가 강한 참조 사이클을 유발할 수 있는 가능성이 있는 상황을 보여줍니다. 이 시나리오는 약한 참조로 해결하는 것이 가장 좋습니다.
  • Customer 와 CreditCard 예제는 nil 이 허용되는 하나의 프로퍼티와 nil 일 수 없는 프로퍼티가 강한 참조 사이클을 유발할 수 있는 가능성이 있는 상황을 보여줍니다. 이 시나리오는 미소유 참조로 해결하는 것이 가장 좋습니다.
  • 그러나 두 프로퍼티 모두 항상 값이 있고 초기화가 완료되면 nil 이 되어서는 안되는 세번째 시나리오가 있습니다. 이 시나리오에서는 한 클래스의 미소유 프로퍼티를 다른 클래스에 암시적으로 언래핑된 옵셔널 프로퍼티와 결합하는 것이 유용합니다.
class Country {
    let name: String
    var capitalCity: City!
    init(name: String, capitalName: String) {
        self.name = name
        self.capitalCity = City(name: capitalName, country: self)
    }
}

class City {
    let name: String
    unowned let country: Country
    init(name: String, country: Country) {
        self.name = name
        self.country = country
    }
}

var country = Country(name: "Canada", capitalName: "Ottawa")
print("\(country.name)'s capital city is called \(country.capitalCity.name)")
// Prints "Canada's capital city is called Ottawa"
  • 두 클래스 간의 상호 종속성을 설정하기 위해 City 에 대한 초기화 구문은 Country 인스턴스를 가지고 있고 country 프로퍼티에 저장합니다.
  • City 에 대한 초기화 구문은 Country 에 대한 초기화 구문 내에서 호출됩니다. 그러나 Country 에 대한 초기화 구문은 새로운 Country 인스턴스가 완벽히 초기화 될 때까지 City 초기화 구문에 self 를 전달할 수 없습니다.
  • 이 요구사항을 처리하려면 Country 의 capitalCity 프로퍼티를 타입 설명의 끝에 느낌표 (City!)로 표시되는 암시적 언래핑된 옵셔널 프로퍼티로 선언합니다. capitalCity 프로퍼티는 다른 옵셔널과 같이 nil 의 기본값을 가지지만 언래핑 할 필요없이 값에 접근할 수 있다는 의미입니다.
  • capitalCity 는 기본 nil 값을 가지므로 새로운 Country 인스턴스는 Country 인스턴스가 초기화 구문 내에서 name 프로퍼티를 설정하는 즉시 새로운 Country인스턴스는 완벽히 초기화 된 것으로 간주합니다. 이것은 Country초기화 구문은 name 프로퍼티가 설정되는 즉시 암시적 self 프로퍼티를 참조하고 전달할 수 있다는 의미입니다. 따라서 Country 초기화 구문은 capitalCity 프로퍼티를 설정할 때 City 초기화 구문에 대한 하나의 파라미터로 self 를 전달할 수 있습니다.
  • 이 모든 것은 강한 참조 사이클을 만들지 않고 단일 구문으로 CountryCity 인스턴스를 생성하고 capitalCity 프로퍼티는 옵셔널 값을 언래핑 하기위해 느낌표를 사용할 필요없이 직접 접근할 수 있다는 의미입니다:
  • 위의 예제에서 암시적 언래핑된 옵셔널의 사용은 모든 2단계 클래스 초기화 구문 요구사항을 충족한다는 의미입니다. capitalCity 프로퍼티는 초기화가 완료되면 옵셔널이 아닌 값처럼 사용되고 접근할 수 있지만 강한 참조 사이클은 피할 수 있습니다. ![[Pasted image 20230130165036.png]]

출처(참고문헌)

연결문서

  • [[6. ARC (1)]]
  • [[8. ARC (3)]]
  • [[4. weak var]]

Tag

  • #Swift/ARC
  • #Swift/ARC/strong
  • #Swift/ARC/weak
  • #Swift/ARC/unowned