티스토리 뷰

@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)
  • 모든 타입의 값을 저장할 수 있다.
  • 타입에 대해 안전하지 않지만 더 유연하다.
  1. 단일 저장소로 모든 프로퍼티 타입 저장 가능

     // 다양한 타입을 하나의 딕셔너리에 저장
     data[\Person.name] = "John"// String
     data[\Person.age] = 25// Int
     data[\Person.birthDate] = Date()// Date
  2. *유연한 데이터 저장 가능 *

    • 런타임에 어떤 타입의 프로퍼티가 추가되더라도 수정 없이 저장 가능하다.
  3. 메모리 효율성

    • 타입별로 별도의 저장소를 만들 필요 없다.
    • 하나의 딕셔너리로 모든 데이터 관리한다.
  4. 타입 안전성과 유연성

     // 저장은 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)
        }
    }
}
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/01   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함