iOS 응용 (swift)
[iOS - swift] Swift에서 Collection 타입이 value 타입으로 설계한 이유 (#class 타입을 struct로 감싸는 것의 의미, #out-of-line mutable storage, 동시성, data race condition)
jake-kim
2024. 7. 5. 01:18
Swift에서 Collection 타입은 모두 value 타입
- reference type은 다중 스레드 환경에서 동기화 문제와 Data race 문제를 발생시키므로 value타입으로 관리하는것이 더욱 용이하기 때문에, Collection 타입은 value type으로 설계
- 하지만 아래 코드처럼 reference 타입을 value 타입으로 감싼 것 뿐이며, struct 내부도 모두 value type이 아님
class SomeClass {}
struct SomeStruct {
let c = SomeClass()
}
- 내부도 모두 value type으로 구성하면 비용이 발생하는데, 아래에서 계속 설명
Large structs의 복사 방식 특징 2가지
- 아래처럼 person을 복사할 때 내부적으로 비용이 많이 발생
let person = Person()
let personB = person
- name, birthday, address, relationships을 복사할때, stored property에 대한 자체 저장소가 별도로 필요함
- 따라서 이 값을 많이 복사할 것으로 예상될땐 stored property만큼 별도의 저장소가 생기므로 훨씬 더 많은 메모리 사용이 될 수 있음
- 이런 것을 해결하기 위해서는 stored property 만큼 매번 복사하지 않도록 class타입과 같은 reference type을 사용하면 해결이 가능
- 하지만 reference type은 다중 스레드 환경에서 동기화 문제와 Data race 문제를 발생시키므로 value타입으로 관리하는것이 더욱 용이
- 두 가지 문제를 해결할 수 있는 방법?
- 변경을 가하려는 참조에서만 객체를 복사되도록 하면 동기화 문제와 많은 메모리 사용 방지가 가능
- 이 방법은 class 유형들을 struct로 감싸면 해결이 가능
class 유형들을 struct로 감싸는 것의 이점
- 아래처럼 구현하면 ValueType 인스턴스 a는 변경을 가하려는 참조에서만 객체를 복사되어서 참조 타입만 쓸때의 동기화 문제와 value type만 쓸때의 오버헤드 문제를 모두 해결 가능
class ReferenceType {
var value: Int
init(value: Int) {
self.value = value
}
}
// Copy-on-Write 구조체
struct ValueType {
private var reference: ReferenceType
init(value: Int) {
self.reference = ReferenceType(value: value)
}
var value: Int {
get { return reference.value }
set {
if !isKnownUniquelyReferenced(&reference) {
reference = ReferenceType(value: newValue)
} else {
reference.value = newValue
}
}
}
}
var a = ValueType(value: 42)
var b = a
b.value = 100
print(a.value) // 42
print(b.value) // 100
* 참고