Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ARC (strong , weak, unowned, 순환 참조) 2 #22

Closed
kimkyuchul opened this issue Jan 1, 2023 · 0 comments
Closed

ARC (strong , weak, unowned, 순환 참조) 2 #22

kimkyuchul opened this issue Jan 1, 2023 · 0 comments

Comments

@kimkyuchul
Copy link
Owner

강한(Strong) 참조

  • 인스턴스의 주소값이 변수에 할당될 때, RC가 증가하면 강한 참조(strong)
  • 인스턴스를 생성하고 사용하던 것이 다 strong이자 강한 참조 - > default 값이 Strong
// strong (강한 참조)

class Human {
    var name: String?
    var age: Int?
    
    init(name: String?, age: Int?) {
        self.name = name
        self.age = age
    }
}

//인스턴스의 주소값이 변수에 할당될 때, RC가 증가하면 강한 참조(strong)
let kim = Human(name: "kim", age: 26) 

클래스 인스턴스간 강한 참조 순환 (Strong Reference Cycles Between Class Instances)

  • 클래스의 인스턴스간 강하게 상호 참조를 하고 있는 경우
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") }
}

//옵셔널 타입의 두 변수를 정의하는데 지정된 Apartment와 Person 인스턴스를 가지는데 nil로 우선 초기화
var john: Person?
var unit4A: Apartment?

//각각 인스턴스를 만들어 변수에 할당
john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")
  • Person이라는 클래스는 변수로 Apartment 클래스의 인스턴스를 소유하고 있고 그 Apartment클래스에서는 변수로 Person형의 인스턴스를 소유

1

  • john 변수는 새로운 Person 인스턴스에 강한 참조를 가지며, unit4A 변수는 새로운 Apartment 인스턴스에 강력 참조를 가진다.
  • john의 apartment, unit4A의 tenant가 nil - > Optional의 기본값이 nil
john!.apartment = unit4A
unit4A!.tenant = john
  • john의 apartment 변수에 unit4A를 unit4A.tenant에 john을 할당
  • 상수 또는 변수에 클래스 인스턴스를 할당 할 때, 해당 인스턴스에 대한 참조 → 기본적으로 강한 참조

2

  • Heap영역에서 서로가 서로를 참조
  • 인스턴스 안의 apartment와 tenant가 각각 Apartment, Person 인스턴스를 참조하고 있는 상황 → Person 인스턴스의 참조 횟수 2, Apartment의 인스턴스 참조 횟수 2
john = nil
unit4A = nil
  • 이 시점에서 nil을 할당해 참조를 해지 → 각각의 deinit이 호출되지 않음.

3

  • Heap 영역에서 서로를 강하게 참조하고 있기 때문이죠 → RC가 0이 아니기 때문
  • john과 unit4A는 각 인스턴스에 대한 참조를 하고 있지 않지만 Person 인스턴스와 Apartment 인스턴스의 변수가 각각 상호 참조를 하고 있어 참조 횟수가 1
  • 저 강한 참조를 해제하고 싶어도 john과 unit4A이 nil이기 때문에 접근할 수 없음
  • 메모리 누수가 발생
  • 이런 상황을 ***강한 순환 참조(Strong Reference Cycles)***라고 함 → 영원히 메모리가 남음

weak (약한 참조)

  • 약한 참조의 핵심
    1. 인스턴스를 참조할 시, RC를 증가시키지 않는다
    2. 참조하던 인스턴스가 메모리에서 해제된 경우, 자동으로 nil이 할당되어 메모리가 해제된다
  • 프로퍼티를 선언한 이후, 나중에 nil이 할당 → weak는 무조건 옵셔널 타입의 변수여야함
  • 강한 순환 참조는 서로가 서로를 강하게 참조하고 있던 것이 문제였으니 둘 중 한 쪽을 weak로 선언
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") }
}

//옵셔널 타입의 두 변수를 정의하는데 지정된 Apartment와 Person 인스턴스를 가지는데 nil로 우선 초기화
var john: Person?
var unit4A: Apartment?

//각각 인스턴스를 만들어 변수에 할당
john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")

//tenant를 weak로 선언해주고, unit4A의 tenant에 john을 넣으면 이제 참조가 시작
unit4A!.tenant = john

4

  • tenant를 weak로 선언해주고, unit4A의 tenant에 john을 넣으면 이제 참조가 시작
    • apartment가 아닌 tenant에 weak를 선언한 이유 → 애플 : 다른 인스턴스의 수명이 더 짧은 경우 즉, 다른 인스턴스를 먼저 할당 해제 할 수 있는 경우 약한 참조를 사용
  • 그러나 weak(약한 참조)를 선언해줬기 때문에 이 시점에서 Person 인스턴스에 대한 참조 회수는 변수 john이 참조하고 있는 1회 뿐
john = nil
// Prints "John Appleseed is being deinitialized"
  • john을 nil로 선언 시 deinit의 print가 찍힌다.

5

  • john을 nil로 선언하는 순간 john이 참조하고 있는 RC 1이 끊어지면서 RC가 0이 됨
  • 참조하는 인스턴스가 메모리에서 해제되면 / 자동으로 weak 참조를 nil로 설정
  • 순환 참조이지만 weak로 선언되어 RC 값을 올리지 않는 것 → 약한 순환 참조

unowned (미소유 참조)

  • unowned과 weak는 강한 순환 참조를 해결 할 수 있음 (RC 값을 증가시키지 않음)
  • 약한 참조와는 다르게 미소유 참조는 항상 값이 있음을 가정 (뒤로가면 이해됨..)
  • weak와 반대로 다른 인스턴스의 수명이 동일하거나 수명이 더 긴 경우 unowned(미소유) 참조를 사용
  • 참조하는 인스턴스가 메모리에서 해제되어도 자동으로 nil로 만들어주지 않음.
    • unowned 참조는 weak 참조와 달리 항상 값을 가질 것으로 예상 → ARC는 이 unowned값을 nil로 설정 X
    • nil로 변경되지 않으므로 let으로 선언가능
    • 참고) swift5.0부턴 unowned 프로퍼티가 이제 Optional타입을 지원한다고 함
  • 내가 unowned로 참조하고 있는 인스턴스가 메모리에서 나보다 먼저 해제가 안되면 되는것에 사용
// 고객은 신용카드가 없을 수 있다.
// 하지만 신용카드가 고객이 없을 수 없다. 항상 신용카드는 고객과 연결

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는 card 변수로 CreditCard인스턴스를 참조하고 있고 CreditCard는 customer로Custome
    인스턴스를 참조
  • customer는 미소유 참조 unowned로 선언 → 이유는 고객과 신용카드를 비교해 봤을때 신용카드는 없더라도 사용자는 남아있을 것이기 때문 (신용카드가 고객이 없을 수 없기 때문에 신용카드보다 고객이 먼저 없어지는 경우는 없음)
  • 사용자는 항상 존재 그래서 CreditCard에 customer를 unowned로 선언
var john: Customer?

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

6

  • John이 Customer 인스턴스를 참조하고 있고 CreditCard 인스턴스도 Customer Instance를 참조하고 있지만 미소유(unowned) 참조를 하고 있기 때문에 Customer 인스턴스에 대한 참조 횟수는 1회
john = nil
// prints "John Appleseed is being deinitialized"
// prints "Card #1234567890123456 is being deinitialized"

7

  • john을 nil로 할당하는 순간 위 강한(strong) 참조가 없어지면서 RC가 0이 되면서 순환참조가 없어짐

참고자료

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant