SwiftData — @Attribute

在 Core Data 中,@Attribute(.transformable) 是 Core Data 中用来定义一个可变换类型属性的属性包装器。它允许你在实体模型中存储复杂类型的数据(比如自定义对象、数组、字典等),并通过一个转换器(NSValueTransformer)将这些数据变换为可以存储在数据库中的二进制形式。

换句话说,它是用来支持存储非基本类型的数据,比如 UIColorUIImage 或自定对象到 Core Data 的功能。


背景:Core Data 和 Transformable #

在 Core Data 中,表格数据库的某些字段只能存储基本的原生数据类型(比如 IntStringDouble 等)。对于自定义类型,Core Data 并不直接支持,这就是 Transformable(可变换类型) 的用途。

通过将 @Attribute(.transformable) 转换为可变换属性:

  • Core Data 自动将自定义对象序列化(例如将其转换为 DataJSON)。
  • 在从存储中取回时,也可以反序列化为原始对象。

@Attribute(.transformable) 是 Swift 5 中的一个属性包装器,用来启用 Core Data 的 Transformable 数据类型。


@Attribute(.transformable) 的作用 #

@Attribute(.transformable) 提供了一种声明式方法,让你在 Swift 定义实体属性时,直接将某个存储的属性定义为一个 可变换类型

在 Core Data 的模型文件中,“Transformable” 字段可以自动存储非标量数据类型(如自定义类、数组、字典等)通过归档(serialization)的方式将数据存储为二进制数据。


如何使用 @Attribute(.transformable) #

1. 属性声明 #

我们可以通过在 CoreData 模型中使用属性包装器 @Attribute(.transformable) 来标记某些需要存储的非基本自定义数据类。

示例代码:

import CoreData

@objc(SampleEntity)
class SampleEntity: NSManagedObject {
    @Attribute(.transformable)
    var customObject: CustomData?
}

2. 自定义数据类型 #

Transformable 类型的属性支持存储对象类型,例如你自定义的类或结构(需要 Codable 支持),比如以下定义一个自定义的数据类型:

import Foundation

struct CustomData: Codable {
    var name: String
    var age: Int
}

通过将自定义对象符合 Codable,Core Data 将知道如何将其序列化为 Data 并存储。


3. 创建 Managed Object 子类 #

在管理对象模型中设置和获取 customObject 时,Core Data 会根据你定义的规则自动存储和加载保存的数据。例如:

let context = persistentContainer.viewContext

// 创建一个对象
let entity = SampleEntity(context: context)
entity.customObject = CustomData(name: "Alice", age: 25)

// 保存到 Core Data
do {
    try context.save()
} catch {
    print("Error saving context: \(error)")
}

// 读取 Core Data 中的数据
let fetchRequest: NSFetchRequest<SampleEntity> = SampleEntity.fetchRequest()
do {
    let results = try context.fetch(fetchRequest)
    if let savedObject = results.first?.customObject {
        print("Name: \(savedObject.name), Age: \(savedObject.age)")
    }
} catch {
    print("Fetch failed: \(error)")
}

用途或适用场景 #

使用 @Attribute(.transformable) 很适合以下场景:

  1. 存储复杂的自定义类型

    • 例如你定义了一个包含属性的对象(如类,或者 Codable 结构体)。
  2. 存储非标准类型的 UIKit 数据

    • 比如 UIColorUIImage 等,不能直接存储进 Core Data。
  3. 需要序列化的非基本数据类型

    • 比如字典(Dictionary)、数组(Array)以及其他非标量数据。

持久化过程(幕后原理) #

@Attribute(.transformable) 实现的大致机制:

  1. 属性数据的序列化
    • Swift 使用 NSKeyedArchiver 或者 Codable/JSONEncoder 来将数据存储为二进制数据 (Data)。
  2. 存储到 Core Data
    • Core Data 会将这些序列化后的二进制数据保存到数据库中。
  3. 属性数据的反序列化
    • 当数据被读取时,Core Data 会利用 NSKeyedUnarchiver 或者对应的解码器,将数据转换为原来的具体类型。

注意:

  • 如果编码和解码不是标准的,那么你需要提供自定义的转换逻辑(可以使用 NSValueTransformer 或者 Codable)。

更多关于 Transformer: 自定义 NSValueTransformer #

对于非标准化的类型,比如你需要特殊的序列化方式,可以自定义 NSValueTransformer。

示例:自定义 UIColor 保存到 Core Data #

  1. 自定义 UIColorTransformer
import UIKit
import CoreData

class UIColorTransformer: ValueTransformer {
    override func transformedValue(_ value: Any?) -> Any? {
        guard let color = value as? UIColor else {
            return nil
        }
        return try? NSKeyedArchiver.archivedData(withRootObject: color, requiringSecureCoding: true)
    }

    override func reverseTransformedValue(_ value: Any?) -> Any? {
        guard let data = value as? Data else {
            return nil
        }
        return try? NSKeyedUnarchiver.unarchivedObject(ofClass: UIColor.self, from: data)
    }
}
  1. 注册自定义 ValueTransformer:
ValueTransformer.setValueTransformer(UIColorTransformer(), forName: NSValueTransformerName("UIColorTransformer"))
  1. 配置 Core Data 模型:
    • 在 Core Data 模型中,为 UIColor 类型的 Transformable 属性设置 Transformer 为 UIColorTransformer

使用注意事项 #

  1. 性能问题: Storing Transformable 数据会增加序列化和反序列化的开销,不适合用于高频繁的数据存储。

  2. 兼容性问题

    • 如果你修改了自定义类型的数据结构(比如新增了属性),需要处理解码时的向后兼容性问题。
  3. 数据迁移问题: Transformable 类型的字段如果发生变化,可能会引发数据迁移问题,建议小心使用。


总结 #

  • @Attribute(.transformable) 是 Core Data 用于支持非基本类型数据(如对象、数组、自定义结构体)的存储方式。
  • 它的本质是通过编码和解码将数据存储为二进制,并在需要时提供合适的反序列化方法。
  • 适用于存储复杂的自定义数据类型,但需要注意处理性能开销和兼容性问题。
本文共 1691 字,上次修改于 Jan 7, 2025