티스토리 뷰
@Model 살펴보기
1. BackingData
@Transient
private var _$backingData: any SwiftData.BackingData<ChatObject>
public var persistentBackingData: any SwiftData.BackingData<ChatObject>
- SwiftData가 실제 데이터를 저장하고 관리하는 방식이다.
@Transient
는 이 프로퍼티가 영구 저장소에 저장되지 않음을 나타낸다.- BackingData는 실제 데이터 저장소와의 연결을 관리한다.
class BackingData<T> {
// 실제 데이터를 저장하는 딕셔너리
var data: [PartialKeyPath<T>: Any] = [:]
// 데이터 읽기
func get<V>(_ key: KeyPath<T, V>) -> V {
data[key] as! V
}
// 데이터 쓰기
func set<V>(_ key: KeyPath<T, V>, _ value: V) {
data[key] = value
}
}
@Transient private var _$backingData: any SwiftData.BackingData<ChatObject>
- 실제 데이터를 저장하는 내부 저장소이다.
- @Transient로 데이터베이스에 저장되지 않도록 한다.
- 메모리상의 임시 데이터로만 사용한다.
public var persistentBackingData: any SwiftData.BackingData<ChatObject>
SwiftData가 모델의 변경사항을 추적하는 인터페이스이다.
실제 데이터베이스와의 동기화에 사용된다.
실제 아래와 같이 구현된다.
class ChatObject {
var message: String {
get {
_$backingData.get(\.message)
}
set {
_$backingData.set(\.message, newValue)
// 변경사항을 SwiftData에 알림
persistentBackingData.set(\.message, newValue)
}
}
}
- swift github에서 BackingData 클래스를 살펴볼 수 있다.
- 타입 안전성
- 제네릭
<T>
를 사용하여 특정 모델 타입에 대한 데이터만 저장한다. KeyPath
를 통해 타입 안전한 프로퍼티 접근한다.
- 제네릭
- 동적 저장소
[PartialKeyPath<T>: Any]
딕셔너리로 모든 타입의 프로퍼티 값을 저장 가능하다.- 런타임에 프로퍼티 값을 변경하고 추적 가능하다.
- 메모리 최적화
- 실제 데이터는 한 곳(딕셔너리)에 집중되어 저장한다.
- 참조 타입으로 구현되어 메모리 공유 가능하다.
- 변경 추적
- 프로퍼티 접근을 중앙화하여 모든 변경사항 추적 가능하다.
- SwiftData가 변경사항을 감지하고 저장소에 반영 가능하다.
PartialKeyPath? KeyPath?
// KeyPath
let nameKeyPath: KeyPath<Person, String> = \Person.name
// PartialKeyPath
let partialKeyPath: PartialKeyPath<Person> = \Person.name
KeyPath
- 구체적인 값 타입을 알고 있다.
- 특정 타입의 값만 저장/접근 가능하다.
- 타입에 대해 안전하지만 유연성이 떨어진다.
PartialKeyPath
- 타입을 지운다. (return Any)
- 모든 타입의 값을 저장할 수 있다.
- 타입에 대해 안전하지 않지만 더 유연하다.
단일 저장소로 모든 프로퍼티 타입 저장 가능
// 다양한 타입을 하나의 딕셔너리에 저장 data[\Person.name] = "John"// String data[\Person.age] = 25// Int data[\Person.birthDate] = Date()// Date
*유연한 데이터 저장 가능 *
- 런타임에 어떤 타입의 프로퍼티가 추가되더라도 수정 없이 저장 가능하다.
메모리 효율성
- 타입별로 별도의 저장소를 만들 필요 없다.
- 하나의 딕셔너리로 모든 데이터 관리한다.
타입 안전성과 유연성
// 저장은 PartialKeyPath로 유연하게 data[keyPath] = value // 접근은 KeyPath로 타입을 안전하게 func get<V>(_ key: KeyPath<T, V>) -> V
이런 방식으로 SwiftData는 모델 객체의 모든 프로퍼티를 효율적으로 저장하고 관리할 수 있다.
static var schemaMetadata: [SwiftData.Schema.PropertyMetadata]
static var schemaMetadata: [SwiftData.Schema.PropertyMetadata] {
return [
SwiftData.Schema.PropertyMetadata(
name: "message", // 프로퍼티 이름
keypath: \ChatObject.message, // 프로퍼티 접근 경로
defaultValue: nil, // 기본값
metadata: nil // 추가 메타데이터
),
SwiftData.Schema.PropertyMetadata(
name: "date",
keypath: \ChatObject.date,
defaultValue: nil,
metadata: nil
),
SwiftData.Schema.PropertyMetadata(
name: "direction",
keypath: \ChatObject.direction,
defaultValue: nil,
metadata: nil
)
]
}
- 데이터베이스 테이블 생성
- 프로퍼티 정보 관리
required init(backingData: any SwiftData.BackingData<ChatObject>) {
message = _SwiftDataNoType()
date = _SwiftDataNoType()
direction = _SwiftDataNoType()
self.persistentBackingData = backingData
}
- SwiftData가 데이터베이스에서 객체를 로드할 때 사용된다.
- 각 프로퍼티를
_SwiftDataNoType()
으로 초기화하는 이유:- 실제 값은 backingData에서 나중에 로드된다.
- 메모리 초기화를 위한 더미 값으로 사용한다.
@Transient
private let _$observationRegistrar = Observation.ObservationRegistrar()
- Swift의 새로운 Observation 시스템과 연동된다.
- 프로퍼티 변경을 추적하고 알린다.
@Transient
로 표시되어 데이터베이스에 저장되지 않는다.
class ChatObject {
var message: String {
get {
_$observationRegistrar.access(self, keyPath: \.message)
return backingData.get(\.message)
}
set {
_$observationRegistrar.willSet(self, keyPath: \.message)
backingData.set(\.message, newValue)
_$observationRegistrar.didSet(self, keyPath: \.message)
}
}
}
'[Swift] 이것저것' 카테고리의 다른 글
[Swift] TCA에서의 스택 오버플로 및 Copy-On-Write (1) | 2025.01.07 |
---|---|
[Swift Concurrency] TMDB API를 사용한 Task, TaskGroup, Async-let 성능 비교 (3) | 2024.12.03 |
[Swift Concurrency] Continuation 내부 코드 뜯어보기 (0) | 2024.11.21 |
[Swift Concurrency] Task 내부 코드 뜯어보기 (0) | 2024.11.21 |
[Swift] guard의 모든 것? (옵셔널 바인딩, 조건문) (0) | 2022.04.14 |
댓글