[RxSwift] ARC, 강한 참조 strong, 약한 참조 weak, 강한 참조 순환 문제
iOS/Swift

[RxSwift] ARC, 강한 참조 strong, 약한 참조 weak, 강한 참조 순환 문제

Struct vs Class

Struct 타입과 Class 타입을 비교할 때 가장 많이 하는 말 → Struct는 값 타입, Class는 참조 타입!

  • 값 타입: 전달할 때마다 값을 복사해서 전달
  • 참조 타입: 하나의 인스턴스가 참조를 통해 여러 곳에서 접근
    • 인스턴스가 적절한 시점에 메모리에서 해제되지 않으면 메모리 자원을 낭비하게 됨.

→ 스위프트에서 메모리 사용을 관리하기 위해 ARC를 사용!

ARC의 작동 규칙을 모르고 사용하면 인스턴스가 메모리에서 영원히 해제되지 않을 가능성이 있기 때문에... ARC에 대해 알아야 한다.

 

ARC


* 자동 참조 카운팅, Automatic Reference Counting => 자동으로 메모리를 관리해 주는 방식

* 더 이상 필요하지 않은 클래스의 인스턴스를 메모리에서 해제시킴

 -> 더 이상 필요하지 않은 클래스를 판단하기 위해 참조 횟수를 카운트!

 

* RC(Reference Counting)가 0이 되는 순간 인스턴스는 메모리에서 해제됨

 

* 인스턴스가 계속 메모리에 남아 있어야 할 무언가 명분이 필요하다! → 강한(Strong) 참조를 사용하여 RC를 1 증가시킴

 

강한 참조


클래스의 프로퍼티 등을 선언할 때 딱히 무엇인가를 명시하지 않으면 기본으로 강한 참조

// 이 코드에서 말하는 RC(Reference Counting)는 Person 클래스의 인스턴스 RC!!!
class Person {
	let name: String
		
	init(name: String) {
		self.name = name
	}
}

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

reference1 = Person(name: "JUNGBIN") // RC = 1
reference2 = reference1 // RC = 2
reference3 = reference1 // RC = 3

reference3 = nil // RC = 2
reference2 = nil // RC = 1
reference1 = nil // RC = 0 -> 메모리에서 해제됨!!!!

 

강한 참조 순환 문제

* 두 인스턴스가 서로를 참조하는 상황에서 강한 참조 순환 문제가 발생할 수 있음

class Person {
	let name: String
		
	init(name: String) {
		self.name = name
    }
		
	var car: Car?
}

class Car {
	let name: String

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

	var owner: Person?
}

var jungbin: Person? = Person(name: "JUNGBIN") // Person RC = 1
var myCar: Car? = Car(name: "BoongBoong") // Car RC = 1

myCar?.owner = jungbin // Person RC = 2
jungbin?.car = myCar // Car RC = 2

jungbin = nil // Person RC = 1
myCar = nil // Car RC = 1

* 위 코드에서, jungbin nil을 할당했을 때 Person RC = 1이 됨. jungbin은 이제 nil이지만, myCar.owner를 통해 아직 Person 인스턴스에 참조할 수 있음. Person 인스턴스가 메모리에 살아 있고, Person 인스턴스에 참조할 방법도 남아 있는 상황! 하지만 다음에 myCar nil을 할당했을 때, myCar가 참조하던 인스턴스의 RC는 하나 감소하여 1이 되었음. 근데 이제 myCar.owner Person 인스턴스에 접근하지 못하고, myCar nil이라서 Car 인스턴스에 접근하지 못함. 인스턴스에 접근할 방법은 사라졌는데, 정작 인스턴스들은 메모리에 남아 있기 때문에(RC = 1) 메모리 누수 발생!

 

* 아래 그림에서 ViewController가 화면에서 pop 되고 이제 사용하지 않는데도 불구하고 3번의 self.어쩌고저쩌고 코드에 의해 RC 카운트가 한 번 증가되었기 때문에 4번에서 RC = 1이 됨. ViewController의 인스턴스가 메모리에서 해제되지 않음!!!! → 메모리 누수(Memory Leak)

 

약한 참조


* 강한 참조와 달리, 자신이 참조하는 인스턴스의 참조 횟수를 증가시키지 않음

* 참조하는 타입의 프로퍼티를 선언하는 문장 앞에 weak를 써 주면 그 프로퍼티는 인스턴스를 약한 참조!

* 아래 코드는 약한 참조를 통해 강한 참조 순환 문제를 해결하는 코드

class Person {
	let name: String
		
	init(name: String) {
		self.name = name
	}
	
	var car: Car?
}

class Car {
	let name: String

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

	weak var owner: Person?
}

var jungbin: Person? = Person(name: "JUNGBIN") // Person RC = 1
var myCar: Car? = Car(name: "BoongBoong") // Car RC = 1

myCar?.owner = jungbin // Person RC = 1 약한 참조!
jungbin?.car = myCar // Car RC = 2 강한 참조!

jungbin = nil // Person RC = 0, Car RC = 1
print(myCar?.owner) // Person의 인스턴스가 메모리에 없기 때문에 nil(약한 참조 해서 그럼)

myCar = nil // Car RC = 0

 

* 아래에서 세 번째 라인을 보면... 인스턴스가 메모리에서 해제될 때, 자신의 프로퍼티(car)가 강한 참조를 하던 인스턴스(Car 인스턴스)의 RC를 1 감소시킨다는 것을 알 수 있음

 

강한 참조 순환 문제를 멋지게 해결하였습니다~~~