1. Automatic Reference Counting πŸ‘©β€πŸ’»

Swift λŠ” Automatic Reference Counting (ARC)λ₯Ό μ‚¬μš©ν•΄ μ•±μ˜ λ©”λͺ¨λ¦¬ μ‚¬μš©μ„ κ΄€λ¦¬ν•˜κ³  μΆ”μ ν•œλ‹€. λŒ€λΆ€λΆ„μ˜ 경우 Swift μ—μ„œ κ°œλ°œμžλŠ” λ©”λͺ¨λ¦¬λ₯Ό 관리할 ν•„μš”κ°€ μ—†λ‹€. 이에 λŒ€ν•΄ Apple 은 μ΄λ ‡κ²Œ λ§ν•œλ‹€. just work.

ARC λŠ” Class Instance κ°€ 더 이상 ν•„μš”ν•˜μ§€ μ•Šμ„ λ•Œ λ©”λͺ¨λ¦¬ 할당을 ν•΄μ œ(free up)ν•œλ‹€ (Deinitialization 이 ν˜ΈμΆœλ¨μ„ 의미).

κ·ΈλŸ¬λ‚˜ 일뢀 경우 ARC λŠ” λ©”λͺ¨λ¦¬λ₯Ό κ΄€λ¦¬ν•˜κΈ° μœ„ν•΄ μ½”λ“œ 관계에 λŒ€ν•œ μΆ”κ°€ 정보λ₯Ό μš”κ΅¬ν•œλ‹€. Swift μ—μ„œ ARC λ₯Ό μ‚¬μš©ν•˜λŠ” 것은 Objective-C μ—μ„œ ARC μ‚¬μš©μ— λŒ€ν•œ Transitioning to ARC Release Notes μ—μ„œ μ„€λͺ…ν•œ μ ‘κ·Ό 방식과 μœ μ‚¬ν•˜λ‹€.

Reference counting은 Class Instanceμ—λ§Œ μ μš©λœλ‹€. Structures 와 Enumerations λŠ” Value Types이닀.


2. How ARC Works πŸ‘©β€πŸ’»

Class 의 new Instanceκ°€ 생겨날 λ•Œλ§ˆλ‹€, ARC λŠ” *Instance 의 정보λ₯Ό μ €μž₯ν•˜κΈ° μœ„ν•΄ λ©”λͺ¨λ¦¬ 청크λ₯Ό ν• λ‹Ή (allocates a chunk of memory)ν•œλ‹€. 이 λ©”λͺ¨λ¦¬λŠ” Instance 의 Type 에 λŒ€ν•œ 정보와 Instance 와 μ—°κ΄€λœ Stored Properties 의 값에 λŒ€ν•œ 정보(pointer)λ₯Ό κ°–λŠ”λ‹€.

λ°˜λŒ€λ‘œ 더 이상 Class Instance κ°€ ν•„μš”ν•˜μ§€ μ•Šμ„ 경우, ARC λŠ” Instance 에 μ‚¬μš©λ˜κ³  있던 λ©”λͺ¨λ¦¬ ν• λ‹Ή(free up the memory)을 ν•΄μ œν•΄ λ‹€λ₯Έ ν”„λ‘œμ„ΈμŠ€κ°€ μ‚¬μš©ν•  수 μžˆλ„λ‘ν•œλ‹€.

λ§Œμ•½ ARC κ°€ 아직 μ‚¬μš©μ€‘μΈ Instance 의 λ©”λͺ¨λ¦¬ 할당을 ν•΄μ œν•˜λ©΄, 더 이상 Instance 의 Properties, Methods 에 μ ‘κ·Όν•  수 μ—†μ–΄ 앱에 crash κ°€ λ°œμƒν•œλ‹€.

λ”°λΌμ„œ ARC λŠ” 아직 μ‚¬μš©μ€‘μΈ Instances κ°€ λ©”λͺ¨λ¦¬ ν•΄μ œλ˜μ§€ μ•Šλ„λ‘, 각 Class Instance κ°€ μ–Όλ§ˆλ‚˜ λ§Žμ€ Properties, Constants, Variables λ₯Ό μ°Έμ‘°(referring)ν•˜κ³  μžˆλŠ”μ§€ 좔적해 단 ν•˜λ‚˜μ˜ μ°Έμ‘°(reference)라도 μœ νš¨ν•˜λ‹€λ©΄ Instance 의 할당을 ν•΄μ œ(deallocate)ν•˜μ§€ μ•ŠλŠ”λ‹€.

이것을 κ°€λŠ₯ν•˜λ„λ‘ ν•˜κΈ° μœ„ν•΄ ARC λŠ” Class Instance λ₯Ό Properties, Constants, Variables 에 ν• λ‹Ήν•  λ•Œλ§ˆλ‹€ 이듀 사이에 κ°•ν•œ μ°Έμ‘° (strong reference)λ₯Ό λ§Œλ“ λ‹€. β€œstrong” μ΄λΌλŠ” 단어가 μ‚¬μš©λœ μ΄μœ λŠ” ν•΄λ‹Ή Instances κ°€ λ‚¨μ•„μžˆλŠ” ν•œ ARC λŠ” λ©”λͺ¨λ¦¬ ν• λ‹Ή ν•΄μ œλ₯Ό ν—ˆμš©ν•˜μ§€ μ•ŠκΈ° λ•Œλ¬Έμ΄λ‹€.


3. ARC in Action πŸ‘©β€πŸ’»

ARC 의 λ™μž‘μ„ ν™•μΈν•˜κΈ° μœ„ν•΄ Person μ΄λΌλŠ” Class λ₯Ό ν•˜λ‚˜ μƒμ„±ν•œλ‹€.

class Person {
    let name: String
    init(name: String) {
        self.name = name
        print("\(name) is being initialized")
    }
    deinit {
        print("\(name) is being deinitialized")
    }
}


λ‹€μŒμœΌλ‘œ Person? Types 의 λ³€μˆ˜λ₯Ό 3개 μƒμ„±ν•œλ‹€. Optional Types μ΄λ―€λ‘œ ν•΄λ‹Ή λ³€μˆ˜ 3κ°œλŠ” nil value 둜 μ΄ˆκΈ°ν™” λœλ‹€.

var reference1: Person?
var reference2: Person?
var reference3: Person?


new Person instance λ₯Ό ν•˜λ‚˜ 생성해 reference1 λ³€μˆ˜μ— ν• λ‹Ήν•œλ‹€.

reference1 = Person(name: "John Appleseed")
John Appleseed is being initialized

이제 reference1 λ³€μˆ˜κ°€ Person(name: "John Appleseed") instance λ₯Ό Strong References 둜 κ°–λŠ”λ‹€. λ”°λΌμ„œ ARC λŠ” 이 Person(name: "John Appleseed")에 λŒ€ν•œ Strong References λ₯Ό +1 μ‹œμΌœ 1개λ₯Ό κΈ°μ–΅ν•΄ 이 Instance κ°€ λ©”λͺ¨λ¦¬μ— μœ μ§€λ˜κ³ , deallocated λ˜μ§€ μ•Šλ„λ‘ ν•œλ‹€.


reference2 = reference1

이제 reference2 λ³€μˆ˜ μ—­μ‹œ Person(name: "John Appleseed") instance λ₯Ό Strong References 둜 κ°€μ Έ 이듀 사이에도 Strong References κ°€ μƒμ„±λ˜μ—ˆλ‹€. λ”°λΌμ„œ ARC λŠ” Person(name: "John Appleseed")에 λŒ€ν•œ Strong References λ₯Ό +1 μ‹œμΌœ 2개λ₯Ό κΈ°μ–΅ν•œλ‹€.

그리고 μ—¬κΈ°μ„œ μ€‘μš”ν•œ 것은 new Instanceλ₯Ό μƒμ„±ν•˜λŠ” 것이 μ•„λ‹Œ Original Instance 의 Reference λ₯Ό κ³΅μœ ν•˜λŠ” 것이기 λ•Œλ¬Έμ— Initializer λŠ” ν˜ΈμΆœλ˜μ§€ μ•ŠλŠ”λ‹€.


reference3 = reference1

λ§ˆμ°¬κ°€μ§€λ‘œ 이제 Person(name: "John Appleseed")에 λŒ€ν•œ Strong References λŠ” 3κ°œκ°€ μƒμ„±λ˜μ—ˆλ‹€.


3개의 Strong References 쀑 Original Reference λ₯Ό 포함해 2개λ₯Ό λŠμ–΄λ³΄μž(break).

reference1 = nil
reference2 = nil

ARC λŠ” Person(name: "John Appleseed")에 λŒ€ν•œ Strong References λ₯Ό -2 μ‹œμΌœ 1개λ₯Ό κΈ°μ–΅ν•œλ‹€. λ”°λΌμ„œ 아직 이 Instance κ°€ λ©”λͺ¨λ¦¬μ— μœ μ§€λ˜κ³ , deallocated λ˜μ§€ μ•Šλ„λ‘ ν•œλ‹€.


λ§ˆμ§€λ§‰ 남은 Strong References μ—­μ‹œ λŠμ–΄λ³΄μž.

reference3 = nil
John Appleseed is being deinitialized

ARC λŠ” Person(name: "John Appleseed")에 λŒ€ν•œ Strong References λ₯Ό -1 μ‹œμΌœ μ‘΄μž¬ν•˜μ§€ μ•ŠμŒμ„ 확인(zero strong references)ν•œλ‹€.
λ”°λΌμ„œ 이제 Instance λŠ” deallocated λ˜μ–΄ Deinitializer κ°€ ν˜ΈμΆœλœλ‹€.


4. Strong Reference Cycles Between Class Instances πŸ‘©β€πŸ’»

μœ„μ—μ„œ ARC κ°€ μ–΄λ–»κ²Œ λ™μž‘ν•˜κ³ , Instance λ₯Ό λ©”λͺ¨λ¦¬μ— μœ μ§€ν•˜λŠ”μ§€ ν™•μΈν–ˆλ‹€.

μ΄λ²ˆμ—λŠ” Strong References κ°€ μ ˆλŒ€λ‘œ zero strong references 에 λ„λ‹¬ν•˜μ§€ μ•ŠλŠ” μ½”λ“œμ˜ 예λ₯Ό 보렀 ν•œλ‹€. μ΄λŠ” 두 개의 Classes κ°€ μ„œλ‘œμ— λŒ€ν•œ Strong References λ₯Ό κ°–λŠ” 경우 λ°œμƒν•œλ‹€. 두 Instances λ₯Ό λ™μ‹œμ— ν•΄μ œ(deallocate)ν•  수 μ—†μ–΄ 각 Instances λŠ” μ„œλ‘œλ₯Ό μœ μ§€μ‹œν‚¨λ‹€.

ν•΄λ‹Ή Case λ₯Ό ν™•μΈν•˜κΈ° μœ„ν•΄ Person κ³Ό Apartment λΌλŠ” Classes λ₯Ό μ•„λž˜μ™€ 같이 μƒμ„±ν•œλ‹€.

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 }
    var tenant: Person?
    deinit { print("Apartment \(unit) is being deinitialized") }
}
  • Person class λŠ” μ΄ˆκΈ°κ°’μœΌλ‘œ nil을 κ°–λŠ” Apartment?λ₯Ό Properties 둜 κ°–λŠ”λ‹€.
  • Apartment class λŠ” μ΄ˆκΈ°κ°’μœΌλ‘œ nil을 κ°–λŠ” Person?을 Properties 둜 κ°–λŠ”λ‹€.


μœ„μ™€ λ§ˆμ°¬κ°€μ§€λ‘œ λ³€μˆ˜λ₯Ό μ„ μ–Έν•œλ‹€.

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

λ³€μˆ˜ unit4A은 Apartment(unit: "4A") instance λ₯Ό Strong References 둜 κ°–λŠ”λ‹€.

Strong Reference Cycle 1


Person 은 Apartment λ₯Ό 갖도둝, Apartment λŠ” Person 을 갖도둝 ν•  수 μžˆλ‹€. 이 λ‘˜μ΄ μ„œλ‘œμ˜ Instances λ₯Ό Strong References 둜 갖도둝 ν•΄λ³΄μž.

john?.apartment = unit4A
unit4A?.tenant = john

Strong Reference Cycle 2

이제 Person(name: "John Appleseed")은 λ³€μˆ˜ johnκ³Ό Apartment(unit: "4A") instance 의 λ³€μˆ˜ property tenant에 μ˜ν•΄ μ°Έμ‘°λ˜μ–΄ ARC λŠ” 2개의 Strong References κ°€ μ‘΄μž¬ν•¨μ„ ν™•μΈν•œλ‹€. λ°˜λŒ€μ˜ κ²½μš°λ„ λ§ˆμ°¬κ°€μ§€λ‘œ Apartment(unit: "4A") instance μ—­μ‹œ ARC λŠ” 2개의 Strong References κ°€ μ‘΄μž¬ν•¨μ„ ν™•μΈν•œλ‹€.


λ³€μˆ˜ johnκ³Ό unit4Aκ°€ κ°–λŠ” Strong References λ₯Ό λŠμ–΄λ³΄μž.

john = nil
unit4A = nil

Strong Reference Cycle 3

// Nothing

μ„œλ‘œκ°€ μ„œλ‘œλ₯Ό Strong References 둜 μ°Έμ‘°ν•˜κ³  있기 λ•Œλ¬Έμ— 두 Instances λŠ” μ ˆλŒ€λ‘œ Zero Strong References에 도달할 수 μ—†λ‹€.

λ§Œμ•½ 이걸 λŠμ–΄λ‚΄λ €λ©΄ μ„œλ‘œμ— λŒ€ν•œ Strong References λ₯Ό λ¨Όμ € λŠμ–΄μ•Όν•œλ‹€.

john?.apartment = nil
unit4A?.tenant = nil

john = nil
unit4A = nil
Apartment 4A is being deinitialized
John Appleseed is being deinitialized

ν•˜μ§€λ§Œ 이 방법은 μœ„ν—˜ν•œ 방법이닀. κ°œλ°œμžκ°€ 이λ₯Ό λ†“μΉ˜κ±°λ‚˜ 둜직 μˆœμ„œμƒ λ˜λŠ” 예기치 λͺ»ν•œ μ—λŸ¬ λ“±μœΌλ‘œ 인해 λ³€μˆ˜ johnμ΄λ‚˜ unit4Aκ°€ κ°–λŠ” Strong References 만 λŠμ–΄μ§ˆ 경우 더이상 λ©”λͺ¨λ¦¬λ₯Ό ν•΄μ œν•  수 μ—†λŠ” μƒνƒœκ°€ λ˜λ―€λ‘œ Memory Leak이 λ°œμƒν•œλ‹€.


5. Resolving Strong Reference Cycles Between Class Instances πŸ‘©β€πŸ’»

1. How Resolve Strong Reference Cycles

Swift λŠ” μœ„μ™€ 같은 Strong Reference Cycles 문제λ₯Ό ν•΄κ²°ν•˜κΈ° μœ„ν•΄ 2가지 방법 Weak References와 Unowned Referencesλ₯Ό μ œκ³΅ν•œλ‹€. 이λ₯Ό μ‚¬μš©ν•΄ μ°Έμ‘°ν•˜λ©΄ Reference Cycles 의 ν•œ Instance κ°€ κ°•ν•œ μœ μ§€ 없이 λ‹€λ₯Έ Instance λ₯Ό μ°Έμ‘°ν•  수 μžˆλ‹€. 그러면 Reference Cycles 의 ν•œμͺ½μ˜ μ°Έμ‘°κ°€ Strong References κ°€ μ•„λ‹ˆκ²Œ λ˜λ―€λ‘œ Strong Reference Cycles 없이 μ„œλ‘œλ₯Ό μ°Έμ‘°ν•  수 있고, ν•„μš” μ—†μ–΄μ‘Œμ„ λ•Œ μ—°κ²° 고리λ₯Ό λŠμ–΄λ‚΄κ³  λ©”λͺ¨λ¦¬λ₯Ό ν•΄μ œν•  수 있게 λœλ‹€.

  • μ°Έμ‘°ν•˜λŠ” Instance κ°€ Short Lifetime을 κ°–λŠ” 경우 Weak Referencesλ₯Ό μ‚¬μš©ν•œλ‹€.
  • μ°Έμ‘°ν•˜λŠ” Instance κ°€ Same Lifetime λ˜λŠ” Long Lifetime을 κ°–λŠ” 경우 Unowned Referencesλ₯Ό μ‚¬μš©ν•œλ‹€.

이λ₯Ό μ΄μš©ν•˜λ©΄ Strong References 없이 μ„œλ‘œμ— λŒ€ν•œ Reference Cyclesλ₯Ό κ°€μ§ˆ 수 μžˆλ‹€.

2. Weak References

μœ„ 예제의 경우 Apartment λŠ” tenant κ°€ μžˆμ„ μˆ˜λ„, 없을 μˆ˜λ„ μžˆλ‹€κ³  ν•˜μž. κ·Έλ ‡λ‹€λ©΄ Apartment 에 λΉ„ν•΄ tenant 에 ν• λ‹Ήλ˜λŠ” Person 의 Lifetime 이 Short Lifetime 을 κ°€μ§€λ―€λ‘œ tenant λ₯Ό Weak References둜 λ°”κΎΈλŠ” 것이 μ μ ˆν•˜λ‹€.

Weak References λŠ” Instance λ₯Ό κ°•ν•˜κ²Œ μœ μ§€(strong hold)ν•˜μ§€ μ•ŠλŠ” μ°Έμ‘°μ΄λ―€λ‘œ ARC λŠ” Instance κ°€ ν•΄μ œ(deallocate)λ˜λŠ” 것을 막지 μ•ŠλŠ”λ‹€.

Property Observers λŠ” ARC κ°€ Weak Reference 에 nil 을 μ„€μ •(set)ν•  λ•Œ ν˜ΈμΆœλ˜μ§€ μ•ŠλŠ”λ‹€.


μ•„λž˜ μ˜ˆμ œλŠ” μœ„μ™€ 거의 λ™μΌν•˜μ§€λ§Œ, μ΄λ²ˆμ—λŠ” Apartment 의 tenant λ₯Ό Weak Reference둜 μ„ μ–Έν–ˆλ‹€.

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")

λ³€μˆ˜ unit4A은 Apartment(unit: "4A") instance λ₯Ό Strong References 둜 κ°–λŠ”λ‹€.


μœ„ μ˜ˆμ œμ™€ λ§ˆμ°¬κ°€μ§€λ‘œ 이 λ‘˜μ΄ μ„œλ‘œμ˜ Instances λ₯Ό μ°Έμ‘°ν•˜λ„λ‘ Reference Cycles λ₯Ό λ§Œλ“€μ–΄λ³΄μž.

john?.apartment = unit4A
unit4A?.tenant = john

Weak References 1


μœ„μ™€ λ§ˆμ°¬κ°€μ§€λ‘œ λ³€μˆ˜ johnκ³Ό unit4Aκ°€ κ°–λŠ” Strong References λ₯Ό λŠμ–΄λ³΄μž.

  • Set nil to unit4A variable
print(unit4A as Any)                // Optional(__lldb_expr_13.Apartment)

unit4A = nil
print(unit4A as Any)                // nil
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
    print(john?.apartment as Any)   // Optional(__lldb_expr_13.Apartment)
}

λ³€μˆ˜ unit4A의 Strong References λŠ” λŠμ–΄μ‘Œμ§€λ§Œ Person(name: "John Appleseed")이 Apartment(unit: "4A") instance λ₯Ό Strong References 둜 κ°–κ³  있기 λ•Œλ¬Έμ— ν•΄μ œ(deallocate)λ˜μ§€ μ•ŠλŠ”λ‹€.


  • Set nil to john variable

κ·Έλ ‡λ‹€λ©΄ μ²˜μŒλΆ€ν„° λ‹€μ‹œ μ‹œμž‘ν•΄μ„œ μ΄λ²ˆμ—λŠ” λ³€μˆ˜ john이 κ°–λŠ” Strong References λ₯Ό λŠμ–΄λ³΄μž.

print(john as Any)                  // Optional(__lldb_expr_17.Person)

john = nil
print(john as Any)                  // nil

Weak References 2

John Appleseed is being deinitialized
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
    print(unit4A?.tenant as Any)    // nil
}

λ³€μˆ˜ john의 Strong References κ°€ λŠμ–΄μ§€μž 더이상 Strong References κ°€ 남지 μ•Šμ€ Person(name: "John Appleseed")은 Deinitializers λ₯Ό 호좜 ν›„ ν•΄μ œ(deallocate)λ˜μ—ˆλ‹€.


Person(name: "John Appleseed")이 ν•΄μ œλ˜μ–΄ 이제 Apartment(unit: "4A")λŠ” ν•˜λ‚˜μ˜ Strong References만 λ‚¨κ²Œ λ˜μ—ˆλ‹€. 이제 Apartment(unit: "4A") μ—­μ‹œ ν•΄μ œκ°€ κ°€λŠ₯ν•˜λ‹€.

unit4A = nil

Weak References 3

Apartment 4A is being deinitialized

Garbage Collection을 μ‚¬μš©ν•˜λŠ” μ‹œμŠ€ν…œμ—μ„œλŠ” Garbage Collection 이 trigger 될 λ•Œλ§Œ Strong References κ°€ μ—†λŠ” 객체가 deallocated 되기 λ•Œλ¬Έμ— Simple Caching Mechanism 을 κ΅¬ν˜„ν•˜λŠ”λ° Weak Pointerκ°€ μ‚¬μš©λ˜λŠ” κ²½μš°κ°€ μžˆλ‹€.
즉, Weak Pointer λŠ” Garbage Collection 이 trigger 되기 μ „κΉŒμ§€ μ°Έμ‘°κ°€ κ°€λŠ₯ν•˜λ‹€.

ν•˜μ§€λ§Œ Swift 의 ARCλŠ” 쒀더 tight ν•˜κ²Œ λ©”λͺ¨λ¦¬λ₯Ό κ΄€λ¦¬ν•œλ‹€. ARC λŠ” λ§ˆμ§€λ§‰ Strong References κ°€ μ œκ±°λ˜λŠ” μ¦‰μ‹œ deallocated λ˜μ–΄ Weak References λŠ” μ¦‰μ‹œ μ°Έμ‘°κ°€ λΆˆκ°€λŠ₯ν•˜λ‹€.

3. Unowned References

Weak References 와 λ§ˆμ°¬κ°€μ§€λ‘œ Unowned ReferencesλŠ” μ°Έμ‘°ν•˜λŠ” Instance λ₯Ό κ°•ν•˜κ²Œ μœ μ§€(strong hold)ν•˜μ§€ μ•ŠλŠ”λ‹€. κ·ΈλŸ¬λ‚˜ Weak References 와 λ‹€λ₯΄κ²Œ Unowned References λŠ” μ°Έμ‘°ν•˜λŠ” Instance 의 수λͺ…이 κ°™κ±°λ‚˜(Same Lifetime) 더 κΈ΄(Long Lifetime) 경우 μ‚¬μš©ν•œλ‹€. Weak References 와 λ§ˆμ°¬κ°€μ§€λ‘œ Properties λ˜λŠ” Variables μ„ μ–Έ 전에 unowned keyword μœ„μΉ˜μ‹œμΌœ μ •μ˜ν•œλ‹€.

Weak References 와 달리 Unowned References λŠ” 항상 값을 κ°€μ§ˆ κ²ƒμœΌλ‘œ μ˜ˆμƒλœλ‹€. 결과적으둜 Unowned References λŠ” Value λ₯Ό Optional 둜 λ§Œλ“€μ§€ μ•Šκ³ , ARC λŠ” Unowned References 의 값을 nil 둜 μ„€μ •ν•˜μ§€ μ•ŠλŠ”λ‹€.

References κ°€ 항상 deallocated λ˜μ§€ μ•Šμ€ Instance λ₯Ό μ°Έμ‘°ν•œλ‹€κ³  ν™•μ‹ ν•˜λŠ” κ²½μš°μ—λ§Œ Unowned Referencesλ₯Ό μ‚¬μš©ν•΄μ•Όν•œλ‹€.
즉, Strong References κ°€ μ•„λ‹ˆμ–΄μ„œ ν•΄μ œκ°€ κ°€λŠ₯ν•œλ°, Instance κ°€ deallocated 된 ν›„ μ ‘κ·Όν•  경우 Runtime Errorκ°€ λ°œμƒν•˜κΈ° λ•Œλ¬Έμ΄λ‹€.


λ‹€μŒ μ˜ˆμ œλŠ” Customer와 CreditCardλΌλŠ” 두 Classes λ₯Ό λͺ¨λΈλ‘œ ν•œλ‹€. 이 μ˜ˆμ œλŠ” μ•žμ—μ„œμ˜ Person κ³Ό Apartment λͺ¨λΈκ³Ό 쑰금 λ‹€λ₯Έ 관계λ₯Ό κ°–λŠ”λ‹€. 이 데이터 λͺ¨λΈμ—μ„œ Customer λŠ” CreditCard λ₯Ό 가지고 μžˆκ±°λ‚˜ 가지고 μžˆμ§€ μ•Šμ„ 수 μžˆμ§€λ§Œ, CreditCard λŠ” 항상 Customer 와 μ—°κ²°λ˜μ–΄μžˆλ‹€.

μ•žμ˜ λͺ¨λΈκ³Ό λΉ„κ΅ν•΄λ³΄μž.

1 ) Person κ³Ό Apartment λͺ¨λΈ

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") }
}
  • Person : Apartment(=apartment) 없이 μ‘΄μž¬ν•  수 μžˆλ‹€. init(name:) & var apartment: Apartment?
  • Apartment : Person(=tenant) 없이 μ‘΄μž¬ν•  수 μžˆλ‹€. init(unit:) & var tenant: Person?
  • 그리고 Person 의 Lifetime 이 Apartment 의 Life Cycles 보닀 짧닀.

λ”°λΌμ„œ Lifetime 이 κΈ΄μͺ½μΈ Apartment κ°€ Short Lifetime 을 κ°–λŠ” Person 을 μ°Έμ‘°ν•  λ•Œ weekλ₯Ό λΆ™μ—¬ week var tenant: Person?λ₯Ό λ§Œλ“€μ–΄ μ€€λ‹€.


2 ) Customer 와 CreditCard λͺ¨λΈ

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") }
}
  • Customer : CreditCard(=card) 없이 μ‘΄μž¬ν•  수 μžˆλ‹€. init(name:) & var card: CreditCard?
  • CreditCard : Customer(=customer) 없이 μ‘΄μž¬ν•  수 μ—†λ‹€. init(number:customer:) & let customer: Customer
  • 그리고 Customer 의 Lifetime 이 CreditCard 의 Lifetime 보닀 κΈΈκ±°λ‚˜ κ°™μœΌλ©°, CreditCard λŠ” Customer 에 쒅속적이닀.

λ”°λΌμ„œ Lifetime 이 μ§§κ±°λ‚˜ κ°™μœΌλ©° Customer 에 쒅속성을 κ°–λŠ” CreditCard κ°€ Long Lifetime 을 κ°–λŠ” Customer λ₯Ό μ°Έμ‘°ν•  λ•Œ unownedλ₯Ό λΆ™μ—¬ unowned let customer: Customerλ₯Ό λ§Œλ“€μ–΄ μ€€λ‹€.

CreditCard λŠ” Customer λ₯Ό 항상 κ°–κ³  μžˆμ–΄μ•Ό ν•œλ‹€λŠ” 쒅속성이 있기 λ•Œλ¬Έμ— Strong Reference Cycles λ₯Ό ν”Όν•˜κΈ° μœ„ν•΄ 항상 Unowned References둜 μ •μ˜ν•œλ‹€.


var john: Customer?

john = Customer(name: "John Appleseed")

이제 λ³€μˆ˜ john은 Customer(name: "John Appleseed") instance λ₯Ό Strong References 둜 κ°–λŠ”λ‹€.

그리고 이제 Customer(name: "John Appleseed")이 μ‘΄μž¬ν•˜λ―€λ‘œ Customer 에 쒅속성을 κ°–λŠ” CreditCard instance λ₯Ό 생성할 수 μžˆλ‹€.

john!.card = CreditCard(number: 1234_5678_9012_3456, customer: john!)

Unowned References 1


이제 λ³€μˆ˜ john이 κ°–λŠ” Strong References λ₯Ό λŠμ–΄λ³΄μž.

john = nil

Unowned References 2

ARC λŠ” Customer(name: "John Appleseed")κ°€ zero strong references 에 λ„λ‹¬ν–ˆμŒμ„ ν™•μΈν•˜κ³  Instance λ₯Ό deallocated μ‹œν‚€λ©°, Customer 에 쒅속성을 가지며 Customer 의 Properties 둜써 μ‘΄μž¬ν•˜λ˜ CreditCard μ—­μ‹œ deallocated λœλ‹€.

John Appleseed is being deinitialized
Card #1234567890123456 is being deinitialized

μœ„ μ˜ˆμ œλŠ” μ–΄λ–»κ²Œ Safe Unowned Referencesλ₯Ό μ‚¬μš©ν•˜λŠ”μ§€ 보여쀀닀.

ν•˜μ§€λ§Œ Swift λŠ” μ„±λŠ₯ μƒμ˜ 이유둜 Runtime Safety Checksλ₯Ό λΉ„ν™œμ„±ν™” ν•  수 μžˆλŠ” Unsafe Unowned References μ—­μ‹œ μ œκ³΅ν•œλ‹€. λŒ€μ‹  Unstructured Concurrency 와 λ§ˆμ°¬κ°€μ§€λ‘œ μ™„μ „ν•œ μ±…μž„(completely responsibility for correctness)이 μ‚¬μš©μžμ—κ²Œ 주어진닀.

Unsafe Unowned References 둜 μ½”λ“œλ₯Ό μž‘μ„±ν–ˆκ³ , μ°Έμ‘°ν•˜λ˜ Instance κ°€ deallocated 된 경우, Unsafe Unowned References λŠ” 이λ₯Ό μ•Œ 수 μ—†μ–΄ 기쑴에 가지고 있던 λ©”λͺ¨λ¦¬ μ£Όμ†Œ(Pointer)λ₯Ό μ΄μš©ν•΄ μ•ˆμ „ν•˜μ§€ μ•Šμ€ 접근을 ν•˜κ²Œ λœλ‹€.

4. Unowned Optional References

μœ„ μ˜ˆμ œμ—μ„œλŠ” Unowned Referencesκ°€ Non-Optional μ΄μ—ˆλ‹€. μ΄λ²ˆμ—λŠ” Optional Types 인 Unowned Optional References에 λŒ€ν•΄ μ•Œμ•„λ³Έλ‹€.

ARC Ownership Modelμ—μ„œ Unowned Optional References와 Weak ReferencesλŠ” 같은 contextμ—μ„œ μ‚¬μš©λ  수 μžˆλ‹€.
차이점은 Unowned Optional References λ₯Ό μ‚¬μš©ν•  λ•Œ Valid Object λ₯Ό μ°Έμ‘°ν•˜κ±°λ‚˜ nil 둜 μ„€μ •λ˜μ–΄μžˆλŠ”μ§€ ν™•μΈν•΄μ•Όν•œλ‹€.

그리고 κ°€μž₯ μ€‘μš”ν•œ 것은 Unstructured Concurrency 와 λ§ˆμ°¬κ°€μ§€λ‘œ μ™„μ „ν•œ μ±…μž„(completely responsibility for correctness)이 μ‚¬μš©μžμ—κ²Œ 주어진닀.


λ‹€μŒμ€ ν•™κ΅μ˜ νŠΉμ • 과에 μ œκ³΅λ˜λŠ” κ°•μ˜λ₯Ό μΆ”μ ν•˜λŠ” μ˜ˆμ œλ‹€.

class Department {
    var name: String
    var course: [Course]
    init(name: String) {
        self.name = name
        self.course = []
    }
    deinit { print("Department '\(name)' is being deinitialized") }
}

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
    }
    deinit { print("Course '\(name)' is being deinitialized") }
}

DepartmentλŠ” κ³Όμ—μ„œ μ œκ³΅ν•˜λŠ” κ°•μ˜μ— λŒ€ν•΄ κ°•ν•œ μ°Έμ‘°λ₯Ό κ°–λŠ”λ‹€. 그리고 ARC Ownership Model μ—μ„œ Department λŠ” κ°•μ˜λ₯Ό μ†Œμœ ν•˜κ³  있고,
CourseλŠ” department 와 nextCourse λΌλŠ” 2개의 Unowned Referencesλ₯Ό κ°–λŠ”λ‹€.

그리고 Department 의 Lifetime 이 Course 의 Lifetime 보닀 κΈΈκ±°λ‚˜ κ°™μœΌλ©°, Course 의 department λŠ” Department 에 μ’…μ†μ μ΄λ―€λ‘œ Optional 이 μ•„λ‹ˆλ‹€. ν•˜μ§€λ§Œ Course 의 nextCourse λŠ” μ‘΄μž¬ν•  μˆ˜λ„, μ•Šμ„ μˆ˜λ„ 있기 λ•Œλ¬Έμ— Optional이닀.

var department: Department?
var intro: Course?
var intermediate: Course?
var advanced: Course?

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

intro?.nextCourse = intermediate!
intermediate?.nextCourse = advanced!
department?.course = [intro!, intermediate!, advanced!]

μœ„μ™€ 같이 Horticulture 과에 3개의 κ°•μ˜λ₯Ό κ°œμ„€ν•˜κ³ , λ“±λ‘ν•œ κ²°κ³Όλ₯Ό 그림으둜 ν‘œν˜„ν•˜λ©΄ λ‹€μŒκ³Ό κ°™λ‹€.

Unowned Optional References 1

ν•œλ²ˆ 각각의 λ³€μˆ˜λ“€μ„ 좜λ ₯ν•΄λ³΄μž. μš°μ„  κ°•μ˜λŠ” λ‹€μŒκ³Ό 같이 ν™•μΈλœλ‹€.

let printCourse = { (variableName: String, course: Course) in
    print("""
          [\(variableName)]
          Class : \(course)
          Name : \(course.name)
          Department : \(course.department)
          Next Course : \(course.nextCourse as Any)

          """)
}
printCourse("intro", intro!)
printCourse("intermediate", intermediate!)
printCourse("advanced", advanced!)
[intro]
Class : __lldb_expr_131.Course
Name : Survey of Planets
Department : __lldb_expr_131.Department
Next Course : Optional(__lldb_expr_131.Course)

[intermediate]
Class : __lldb_expr_131.Course
Name : Growing Common Herbs
Department : __lldb_expr_131.Department
Next Course : Optional(__lldb_expr_131.Course)

[advanced]
Class : __lldb_expr_131.Course
Name : Caring for Tropical Plants
Department : __lldb_expr_131.Department
Next Course : nil

κ³Ό 정보도 좜λ ₯ν•΄λ³΄μž.

print("[department] : \(department!),    \(String(describing: department!.name)),    \(String(describing: department!.course))")
[department] : __lldb_expr_131.Department,    Horticulture,    [__lldb_expr_131.Course, __lldb_expr_131.Course, __lldb_expr_131.Course]


μœ„ Unowned References 와 달리 μ‚¬μš©μžκ°€ Classes μ‚¬μ΄μ˜ References λ₯Ό κ΄€λ¦¬ν•˜κ³  deallocated μ‹œν‚€λŠ” 것에 λŒ€ν•΄ μ±…μž„μ„ λ‹€ν•˜μ§€ λͺ»ν–ˆμ„ λ•Œ μ–΄λ–€ 일이 λ°œμƒν•˜λŠ”μ§€ 확인해본닀.

  • Unsafe Unowned References - error case
department = nil
print(department as Any)   // nil
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
    printCourse("intro", intro!)    // error
}
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
    print("1")
    print(intro!.name)
    print(intro!.department)    // error
    print("2")
}
1
Survey of Planets

μ°Έμ‘°ν•˜λ˜ Instance κ°€ deallocated λ˜μ—ˆμœΌλ‚˜, Unsafe Unowned References λŠ” 이λ₯Ό μ•Œ 수 μ—†μ–΄ 기쑴에 가지고 있던 λ©”λͺ¨λ¦¬ μ£Όμ†Œ(Pointer)λ₯Ό μ΄μš©ν•΄ μ•ˆμ „ν•˜μ§€ μ•Šμ€ 접근을 ν–ˆκ³ , 값을 λ°›μ•„μ˜€μ§€ λͺ»ν•΄ 더이상 진행이 λ˜μ§€ μ•Šκ³  λ©ˆμΆ°λ²„λ Έλ‹€. λ§Œμ•½, λ‹€λ₯Έ ν”„λ‘œμ„ΈμŠ€μ— μ˜ν•΄ ν•΄λ‹Ή λ©”λͺ¨λ¦¬ μ£Όμ†Œμ— 값이 μ €μž₯λ˜μ—ˆμœΌλ‚˜ μ˜ˆμƒν•œ 것과 λ‹€λ₯Έ 값이 λ“€μ–΄κ°€ 있고 κ·Έκ±Έ κ°€μ Έμ˜¬ κ²½μš°λŠ” Runtime Error 둜 μ΄μ–΄μ§ˆ μˆ˜λ„ μžˆλŠ” μ‹¬κ°ν•œ 문제λ₯Ό λ°œμƒμ‹œν‚¬ 수 μžˆλ‹€.


  • Unsafe Unowned References - success case

μœ„μ™€ 같은 λ°œμƒν•˜λŠ” 것을 막기 μœ„ν•΄ Course κ°€ 일뢀 deallocated 될 경우, κ·Έ Course λ₯Ό μ°Έμ‘°ν•˜λŠ” 것듀을 λ¨Όμ € λŠμ–΄μ•Όν•˜λ©°, λ§Œμ•½ Department κ°€ deallocated 될 경우, Department 에 쒅속성을 κ°–λŠ” λͺ¨λ“  Course κ°€ unowned var department property 에 μ ‘κ·Όν•˜μ§€ λͺ»ν•˜λ„둝 ν•˜κ±°λ‚˜ λͺ¨λ“  Course λ₯Ό ν•¨κ»˜ deallocated ν•΄μ•Όν•œλ‹€.

do {
    department = nil
    advanced = nil
    intermediate = nil
    intro = nil
}
Department 'Horticulture' is being deinitialized
Course 'Survey of Planets' is being deinitialized
Course 'Growing Common Herbs' is being deinitialized
Course 'Caring for Tropical Plants' is being deinitialized


Optional Value κΈ°λ³Έ Types λŠ” Swift Standard Library 의 Enumeration 인 Optional이닀.
κ·ΈλŸ¬λ‚˜ Optional 은 Value Types 에 unownedλ₯Ό marked ν•  수 μ—†λ‹€λŠ” κ·œμΉ™μ— λŒ€ν•΄ μ˜ˆμ™Έλ‹€.

Class λ₯Ό Wrapping ν•œ Optional 은 Swift Standard Library 의 Enumeration 인 Optional Types μ΄λ―€λ‘œ Container κ°€ Value Type κ°€ λœλ‹€. 즉, Reference Counting을 μ‚¬μš©ν•˜μ§€ μ•ŠμœΌλ―€λ‘œ, Strong References λ₯Ό Optional 둜 μœ μ§€ν•  ν•„μš”κ°€ μ—†λ‹€.

5. Unowned References and Type! Properties

μœ„μ—μ„œ Strong Reference Cycles λ₯Ό 끊기 μœ„ν•œ 2가지 방법(Week References, Unowned References)에 λŒ€ν•΄ λ‹€λ£¨μ—ˆλ‹€.

1 ) 2개의 Properties κ°€ λͺ¨λ‘ nil 을 ν—ˆμš©ν•˜λŠ” μΌ€μ΄μŠ€

Person κ³Ό Apartment μ˜ˆμ œλŠ” 2개의 Properties κ°€ λͺ¨λ‘ nil 을 ν—ˆμš©ν•˜λŠ” κ²½μš°μ— Strong Reference Cycles 이 λ°œμƒν•  κ°€λŠ₯성이 μžˆλŠ” 상황을 보여쀀닀. 이 μ‹œλ‚˜λ¦¬μ˜€λŠ” Week Referencesλ₯Ό μ΄μš©ν•΄ ν•΄κ²°ν•˜λŠ” 것이 κ°€μž₯ μ’‹λ‹€.

2 ) 1개의 Property λŠ” nil 을 ν—ˆμš©ν•˜κ³ , 1개의 Property λŠ” nil 을 ν—ˆμš©ν•˜μ§€ μ•ŠλŠ” μΌ€μ΄μŠ€

Customer 와 CreditCard μ˜ˆμ œλŠ” 1개의 Property λŠ” nil 을 ν—ˆμš©ν•˜κ³ , 1개의 Property κ°€ nil 을 ν—ˆμš©ν•˜μ§€ μ•ŠλŠ” κ²½μš°μ— Strong Reference Cycles 이 λ°œμƒν•  κ°€λŠ₯성이 μžˆλŠ” 상황을 보여쀀닀. 이 μ‹œλ‚˜λ¦¬μ˜€λŠ” Unowned Referencesλ₯Ό μ΄μš©ν•΄ ν•΄κ²°ν•˜λŠ” 것이 κ°€μž₯ μ’‹λ‹€.

3 ) 2개의 Properties κ°€ λͺ¨λ‘ nil 을 ν—ˆμš©ν•˜μ§€ μ•ŠλŠ” μΌ€μ΄μŠ€

λ§ˆμ§€λ§‰μœΌλ‘œ 2개의 Properties κ°€ λͺ¨λ‘ 값이 항상 있고 μ΄ˆκΈ°ν™”κ°€ μ™„λ£Œλ˜λ©΄ nil 이 λ˜μ–΄μ„œλŠ” μ•ˆ λ˜λŠ” μ„Έ 번째 μ‹œλ‚˜λ¦¬μ˜€κ°€ μžˆλŠ” 상황을 μ„€λͺ…ν•œλ‹€. 이 μ‹œλ‚˜λ¦¬μ˜€λŠ” Unowned References 의 λ³€ν˜•μœΌλ‘œ Unowned References 와 Implicitly Unwrapped Optional Properties(Type!)λ₯Ό μ΄μš©ν•΄ ν•΄κ²°ν•œλ‹€.

μ΄λ ‡κ²Œ ν•˜λ©΄ Strong Reference Cycles λ₯Ό ν”Όν•˜λ©΄μ„œ, μ΄ˆκΈ°ν™”κ°€ μ™„λ£Œλ˜λ©΄ 두 Properties λͺ¨λ‘ Optional Unwrapping 없이 μ ‘κ·Όν•  수 μžˆλ‹€.


1 ) Unowned Optional References

Customer 와 CreditCard λͺ¨λΈ κ³Ό λ™μΌν•œ ν˜•νƒœμ˜ μΌ€μ΄μŠ€λ₯Ό λ¨Όμ € ν™•μΈν•˜κ³ , Implicitly Unwrapped Optional Properties κ°€ 적용된 λͺ¨λΈμ„ 확인해 비ꡐ해본닀.

class Country {
    let name: String
    var capitalCity: City?
    init(name: String, capitalName: String) {
        self.name = name
        self.capitalCity = City(name: capitalName, country: self)
    }
    deinit { print("\(name) is being deinitialized") }
}

class City {
    let name: String
    unowned let country: Country
    init(name: String, country: Country) {
        self.name = name
        self.country = country
    }
    deinit { print("Card #\(number) is being deinitialized") }
}
var country = Country(name: "Canada", capitalName: "Ottawa")
print("\(country.name)'s capital city is called \(country.capitalCity!.name)")
Canada's capital city is called Ottawa
  • var capitalCity: City?와 init(name:, country:)λ₯Ό μ‚¬μš©ν•œλ‹€.
  • country.capitalCity!.name와 같이 country 의 capitalCity 에 μ ‘κ·Όν•˜λ €λ©΄ Optional Unwrapping 이 ν•„μš”ν•˜λ‹€.


2 ) Unowned References and Implicitly Unwrapped Optional Properties

class Country {
    let name: String
    var capitalCity: City!
    init(name: String, capitalName: String) {
        self.name = name
        self.capitalCity = City(name: capitalName, country: self)
    }
    deinit { print("\(name) is being deinitialized") }
}

class City {
    let name: String
    unowned let country: Country
    init(name: String, country: Country) {
        self.name = name
        self.country = country
    }
    deinit { print("Card #\(number) is being deinitialized") }
}
var country = Country(name: "Canada", capitalName: "Ottawa")
print("\(country.name)'s capital city is called \(country.capitalCity.name)")
Canada's capital city is called Ottawa
  • var capitalCity: City!와 init(name:, country:)λ₯Ό μ‚¬μš©ν•œλ‹€.
  • country.capitalCity.name와 같이 Optional Unwrapping 없이 country 의 capitalCity 에 접근이 κ°€λŠ₯ν•˜λ‹€.


Country 의 Initializer 의 self.capitalCity = City(name: capitalName, country: self)λ₯Ό μ‚΄νŽ΄λ³΄μž.

City 의 Initializer λŠ” Country κ°€ ν•„μš”ν•˜λ‹€. ν•˜μ§€λ§Œ Two-Phase Initialization μ—μ„œ μ„€λͺ…ν–ˆλ“―이 'self' μ°Έμ‘°λŠ” 'Phase 2' μ—μ„œλ§Œ κ°€λŠ₯ν•˜λ‹€.

λ”°λΌμ„œ β€˜self’ μ°Έμ‘°λ₯Ό μ‚¬μš©ν•˜λ©΄μ„œ, var capitalCity κ°€ Optional 을 ν—ˆμš©ν•˜μ§€ μ•Šλ„λ‘ ν•˜κΈ° μœ„ν•΄ 'City!'둜 ν‘œμ‹œλ˜λŠ” Implicitly Unwrapped Optionals λ₯Ό μ‚¬μš©ν•΄ nil 을 ν• λ‹Ήν•΄ Phase 1 을 μ²˜λ¦¬ν•˜κ³ λ₯Ό ν•˜κ³ , Phase 2 μ—μ„œ λ°˜λ“œμ‹œ μ €μž₯ν•˜λŠ” 방법을 사 μš©ν•œλ‹€.

  • Implicitly Unwrapped Optionals
let possibleString: String? = "An optional string."
let forcedString: String = possibleString! // requires an exclamation point

let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString // no need for an exclamation point


λ§ˆμ§€λ§‰μœΌλ‘œ deallocated ν…ŒμŠ€νŠΈλ₯Ό ν•΄λ³΄μž

var country: Country?

country = Country(name: "Canada", capitalName: "Ottawa")
country = nil
Country Canada is being deinitialized
City Ottawa is being deinitialized

deallocated κΉŒμ§€ μ •μƒμ μœΌλ‘œ μ²˜λ¦¬λœλ‹€.


6. Strong Reference Cycles for Closures πŸ‘©β€πŸ’»

μœ„μ—μ„œ 두 Class Instance Properties 사이에 μƒμ„±λ˜λŠ” Strong Reference Cycles 와 이λ₯Ό μ–΄λ–»κ²Œ ν•΄κ²°ν•˜λŠ”μ§€ 각각의 μ‹œλ‚˜λ¦¬μ˜€μ— λŒ€ν•΄ μ‚΄νŽ΄λ³΄μ•˜λ‹€.

μ΄λ²ˆμ—λŠ” Class Instance 와 Closures 사이에 μƒμ„±λ˜λŠ” Strong Reference Cycles 에 λŒ€ν•΄ μ•Œμ•„λ³Έλ‹€. 이것은 Class Instance Property 에 Closure λ₯Ό ν• λ‹Ήν•˜κ³ , Closure κ°€ 'self' λ₯Ό μ΄μš©ν•΄ μžμ‹ μ΄ μ†ν•œ context 의 Instance Properties/Methods λ₯Ό μΊ‘μ²˜ν•  λ•Œ μƒμ„±λœλ‹€.

class HTMLElement {
    let name: String
    let text: String?

    lazy var asHTML: () -> String = {
        if let text = self.text {
            return "<\(self.name)>\(text)</\(self.name)>"
        } else {
            return "<\(self.name) />"
        }
    }

    init(name: String, text: String? = nil) {
        self.name = name
        self.text = text
    }

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

μœ„ HTMLElement class λŠ” head와 textλ₯Ό λ°›μ•„ HTML 을 λ§Œλ“€μ–΄μ€€λ‹€.

var heading: HTMLElement? = HTMLElement(name: "h1")
let html = heading!.asHTML()
print(html)         // <h1 />

var headingWithText: HTMLElement? = HTMLElement(name: "p", text: "Hello~")
let anotherHtml = headingWithText!.asHTML()
print(anotherHtml)  // <p>Hello~</p>

asHTML property λŠ” String 값을 HTML Rendering 으둜 좜λ ₯ν•  λ•Œλ§Œ ν•„μš”ν•˜λ―€λ‘œ lazy property 둜 μ„ μ–Έλœλ‹€.
그리고 lazy둜 μ„ μ–Έλ˜μ—ˆμœΌλ―€λ‘œ Property κ°€ ν˜ΈμΆœλ˜λŠ” μ‹œμ μ—λŠ” 이미 Initialization 을 마친 μƒνƒœλ₯Ό μ˜λ―Έν•œλ‹€. 즉, self μ°Έμ‘°κ°€ κ°€λŠ₯함을 μ˜λ―Έν•œλ‹€.


이제 μ‚¬μš©μ΄ λλ‚¬μœΌλ‹ˆ deallocated μ‹œμΌœλ³΄μž.

heading = nil
headingWithText = nil
print(heading as Any)           // nil
print(headingWithText as Any)   // nil
// Nothing

λ³€μˆ˜ headingκ³Ό headingWithText에 μ—°κ²°λœ Strong Reference Cycles λŠ” μ œκ±°λ˜μ—ˆμ§€λ§Œ 두 Classes λͺ¨λ‘ deallocated λ˜μ§€ μ•ŠλŠ”λ‹€.

  • 두 Classes κ°€ κ°–λŠ” Properties 사이에 μƒμ„±λœ References κ°€ Strong Reference Cycles λ₯Ό μƒμ„±ν•˜λŠ” μ΄μœ λŠ” Classes κ°€ Reference Typesμ΄κΈ°λ•Œλ¬Έμ΄λ‹€.
  • 그리고 Closures μ—­μ‹œ Reference Types μ΄λ―€λ‘œ, Classes 와 Closures 사이에도 Strong Reference Cycles κ°€ μƒμ„±λœλ‹€.


Closure Reference Cycles

  • Class Instances λŠ” context 내에 μ •μ˜λœ Properties λ˜λŠ” Methods 의 Pointer λ₯Ό Strong References 둜 μ°Έμ‘°ν•œλ‹€.
    (이 경우 asHTML은 μžμ‹ μ˜ Closure () -> String을 κ°•ν•œ 참쑰둜 κ°–λŠ”λ‹€.)
  • Closures λŠ” μžμ‹ μ΄ μ†ν•œ Context 내에 μ •μ˜λœ Properties λ˜λŠ” Methods 의 Pointer λ₯Ό Strong References 둜 μ°Έμ‘°ν•œλ‹€.
    (이 경우 () -> String은 μžμ‹ μ΄ μ†ν•œ Context 내에 μ •μ˜λœ name, text 에 μ ‘κ·Όν•˜κΈ° μœ„ν•΄ selfλ₯Ό κ°•ν•œ 참쑰둜 κ°–λŠ”λ‹€.)

Closures λŠ” μ—¬λŸ¬ 번 μ°Έμ‘°ν•˜μ§€λ§Œ 단 ν•˜λ‚˜μ˜ selfλ₯Ό Strong References 둜 μΊ‘μ²˜ν•œλ‹€.


7. Resolving Strong Reference Cycles for Closures πŸ‘©β€πŸ’»

1. Defining a Capture List

Swift λŠ” 이 문제λ₯Ό ν•΄κ²°ν•˜κΈ° μœ„ν•΄ Closure Capture Listλ₯Ό μ΄μš©ν•œ μš°μ•„ν•œ ν•΄κ²° 방법을 μ œκ³΅ν•œλ‹€. Capture List λŠ” Closures κ°€ ν•˜λ‚˜ λ˜λŠ” κ·Έ μ΄μƒμ˜ Reference Types λ₯Ό μΊ‘μ²˜ν•  λ•Œ μ‚¬μš©ν•  κ·œμΉ™μ„ μ •μ˜ν•œλ‹€. 두 Classes 의 κ²½μš°μ™€ λ§ˆμ°¬κ°€μ§€λ‘œ Week References λ˜λŠ” Unowned Referencesλ₯Ό μ‚¬μš©ν•œλ‹€.


  • Without Capture List
lazy var someClosure = {
    (index: Int, stringToProcess: String) -> String in
    // closure body goes here
}
lazy var someClosure = {
    // closure body goes here
}

Closures λŠ” Parameter List λ₯Ό context λ‘œλΆ€ν„° μœ μΆ”ν•  수 μžˆμ–΄ μƒλž΅μ΄ κ°€λŠ₯ν•˜λ‹€.


  • With Capture List
lazy var someClosure = {
    [unowned self, weak delegate = self.delegate]
    (index: Int, stringToProcess: String) -> String in
    // closure body goes here
}
lazy var someClosure = {
    [unowned self, weak delegate = self.delegate] in
    // closure body goes here
}

Parameter List λ₯Ό context λ‘œλΆ€ν„° μœ μΆ”ν•˜λ„λ‘ μƒλž΅ν•˜λ”λΌλ„ Capture List λ₯Ό μ‚¬μš©ν•  λ•ŒλŠ” in keyword λ₯Ό λˆ„λ½ν•  수 μ—†λ‹€.

2. Weak and Unowned References

두 Classes 의 κ²½μš°μ™€ λ§ˆμ°¬κ°€μ§€λ‘œ Closures κ°€ μΊ‘μ²˜ν•œ References κ°€ nil이 될 κ°€λŠ₯성이 μžˆλŠ”μ§€μ™€ Lifetime 을 비ꡐ해 μ‚¬μš©ν•œλ‹€.

  • Week References : μΊ‘μ²˜ν•œ self κ°€ nil이 될 κ°€λŠ₯성이 μžˆλŠ” 경우(Short Lifetime) μ‚¬μš©ν•œλ‹€. 즉, Week References λŠ” 항상 Optional이닀.
  • Unowned References : μΊ‘μ²˜ν•œ self κ°€ nil이 될 κ°€λŠ₯성이 μ—†κ³  항상 μ„œλ‘œλ₯Ό μ°Έμ‘°ν•˜λŠ” 경우(Same Lifetime) μ‚¬μš©ν•œλ‹€. 즉, Unowned References λŠ” Forced Unwrapping λ˜λŠ” Non-Optional이닀.

λ‹€μŒμ€ Strong Reference Cycles for Closures 에 Capture List λ₯Ό μ μš©ν•œ μ½”λ“œλ‹€.

class HTMLElement {
    let name: String
    let text: String?

    lazy var asHTML: () -> String = {
        [unowned self] in
        if let text = text {
            return "<\(name)>\(text)</\(name)>"
        } else {
            return "<\(name) />"
        }
    }

    init(name: String, text: String? = nil) {
        self.name = name
        self.text = text
    }

    deinit { print("\(name) is being deinitialized") }
}
var headingWithText: HTMLElement? = HTMLElement(name: "p", text: "Hello~")

headingWithText = nil
p is being deinitialized

deallocated κΉŒμ§€ μ •μƒμ μœΌλ‘œ μ²˜λ¦¬λœλ‹€.




Reference

  1. β€œAutomatic Reference Counting.” The Swift Programming Language Swift 5.7. accessed Mar. 08, 2023, Swift Docs Chapter 24 - Automatic Reference Counting.