Struct vs Class 시리즈
주제
어떤 타입이 참조타입 프로퍼티를 가지면, 자신도 참조타입(class)로 정의해야 한다?
예로, 아래 코드의 LinkedList 구조를 가지는 Queue처럼 참조타입 프로퍼티로를 반드시 가져야만 하는 경우가 있습니다.
struct Queue {
private var head: Node?
private weak var tail: Node?
func enqueue(_ value: Int) { }
func dequeue() -> Int { }
}
class Node {
let value: Int
private(set) var next: Node?
}
외부코드에서 Queue 인스턴스를 참조로 사용해야만 하는 이유가 없다면 일반적으로 struct로 사용이 가능하긴 합니다. 하지만 구조체 Queue
는 일반적인 값타입과는 사뭇 다르게 동작합니다. 아래 예시를 봅시다
let queue1 = Queue()
queue1.equeue(1)
let queue2 = queue1
queue2.enqueue(2)
print(queue1.dequeue()) //print "1"
print(queue1.dequeue()) //print "2" ????
위 코드의 문제점은, 값타입인 Queue의 인스턴스를 다른 상.변수에 할당하며 copy가 발생하지만 내부 프로퍼티인 head
/tail
은 하나의 노드를 공유하는 상황이 발생합니다. 그래서 위와 같이 queue1
이 1회의 enqueue에도 2회의 dequeue가 가능해져버렸습니다
이는 일반적으로 값타입 인스턴스에게 기대하는 상황이 아니기에 협업자에게 혼동을 줄 수 있을 것 같습니다. 결정적으로 이런 특징으로 인해, 값타입을 사용함으로써 기대하는 현재 scope이 종료되면 메모리 해제를 보장받을 수 있다던가 하는 등의 장점들을 취할 수 없게 됩니다
이 외에도 struct의 장점으로 reference counting 관리 비용이 들지 않는다는 점이 있습니다. 하지만 이렇게 reference 프로퍼티를 가져버리면 결국 자신이 value 타입임에도 reference counting 비용을 요구하게 되어 성능상 크게 유리하지 않습니다
결론
어떤 타입이 참조타입을 저장 프로퍼티로 가져야 하는 경우, 자신이 값타입이더라도 '다른 변수에 할당할 때 완전한 copy가 일어나지 않는 점'과 '자신이 메모리 해제되더라도 내부 프로퍼티는 해제될지 알 수 없는 점' 등 값타입의 장점들을 취할 수 없게 됩니다. 또한, 일반적인 참조타입처럼 reference counting의 대상이 되어 성능면에서 크게 유리하지 않습니다
오히려 일반적인 값타입과는 다르게 동작한다는 점 때문에 협업자에게 혼동을 줄 수 있으므로 자신도 class인 것이 합당하리라 판단됩니다