CoreData 在 iOS 项目的学习与使用经验分享(一)

49 阅读3分钟

CoreData是一个强大的框架,用于在Apple的iOS和macOS平台上以对象图形方式管理对象的生命周期和对象图形的持久化。以下是一些个人总结的关键的学习要点,(个人学习笔记,备忘):

CoreData栈的建立与管理:

CoreDataStackManager类是作为单例模式实现的,确保整个应用中有一个统一的数据管理实例。 使用NSPersistentContainer和NSPersistentCloudKitContainer来管理本地和云数据存储。这展示了如何根据用户状态(例如VIP状态)来决定数据存储方式。

// 
class CoreDataStackManager {
// 单例模式 - 确保应用中有一个统一的数据管理实例
static let shared = CoreDataStackManager()
// CoreData 的 Persistent 容器,用于本地数据存储
private var persistentContainer: NSPersistentContainer?
CloudKitPersistent 容器,用于云端数据存储
private var cloudKitContainer: NSPersistentCloudKitContainer?
全局变量,用于跟踪迁移重试的次数
var migrationRetryCount = 0

// 私有初始化方法,防止外部实例化
private init() {
    initializeContainer()
}

// 初始化容器
private func initializeContainer() {
    // 根据用户状态选择合适的容器
    if UserStatusManager.shared.isVIP() {
        setupCloudKitContainer() // VIP用户使用CloudKit容器
    } else {
        setupPersistentContainer() // 非VIP用户使用本地Persistent容器
    }
}

// 设置 容器
private func setupCloudKitContainer() {
    let container = NSPersistentCloudKitContainer(name: "FujiLifeStyle")

    //配置容器的各种设置
    // 省略具体配置细节代码...

    self.cloudKitContainer = container
}

// 设置 Persistent 容器
private func setupPersistentContainer() {
    let container = NSPersistentContainer(name: "FujiLifeStyle")

    //配置容器的各种设置
    // 省略具体配置细节代码...

    self.persistentContainer = container
}

// 获取当前使用的容器
public func getContainer() -> NSPersistentContainer? {
    return cloudKitContainer ?? persistentContainer
}

// 切换容器
public func switchContainer(toVIP: Bool) {
    // 根据新的用户状态初始化对应的容器
    if toVIP {
        setupCloudKitContainer() // 切换到Cloud容器
    } else {
        setupPersistentContainer() // 切换到Persistent容器
    }

    // 这里也可以添加其他逻辑,例如数据迁移等
    // 省略数据迁移相关代码...
}

‘’‘

上下文的使用:

NSManagedObjectContext的获取和使用对于在CoreData中进行CRUD(创建、读取、更新、删除)操作至关重要。 performTask方法展示了如何根据执行环境(主线程或后台线程)来使用不同的上下文进行数据操作,确保数据操作的线程安全。

获取 NSManagedObjectContext
public func getContext() -> NSManagedObjectContext? {
    return getContainer()?.viewContext
}

数据模型的CRUD操作:

// 增加 - Create
public func createEntity<T: NSManagedObject>(ofType type: T.Type, withData data: [String: Any]) -> T? {
    guard let context = getContext(), let entityName = NSStringFromClass(type).components(separatedBy: ".").last else {
        return nil
    }
    
    if let newObject = NSEntityDescription.insertNewObject(forEntityName: entityName, into: context) as? T {
        for (key, value) in data {
            newObject.setValue(value, forKey: key)
        }
        return newObject
    }
    return nil
}


读取(Read)

// 查询 - Read
public func fetchEntities<T: NSManagedObject>(...) -> [T]? {
    // ... 准备请求和执行
    do {
        let results = try context.fetch(fetchRequest)
        return results
    } catch {
        print("Fetch Error: \(error)")
        return nil
    }
}

更新(Update)

// 更新 - Update
public func updateEntity(entity: NSManagedObject, withData data: [String: Any]) {
    for (key, value) in data {
        entity.setValue(value, forKey: key)
    }
}

更新操作通过直接修改NSManagedObject的属性值来进行。此方法接受一个实体和一个键值对字典,然后更新实体的相应属性。

删除(Delete)

// 删除 - Delete
public func deleteEntity(entity: NSManagedObject) {
    guard let context = getContext() else {
        return
    }
    context.delete(entity)
}

高级查询和过滤:

CoreDataStackManager类中,高级查询和过滤是通过使用复合谓词(NSCompoundPredicate)实现的。这些高级技术对于构建灵活和高效的数据库查询是至关重要的。同时,正确地保存上下文和维护数据一致性也是非常重要的。 这是我自己使用的谓词查询。

    var predicates: [NSPredicate] = []
    
    // 添加基于filmsimulation的谓词
    if let filmsimlutation = filmsimlutation, !filmsimlutation.isEmpty {
        let simulationPredicate = NSPredicate(format: "filmSimulationID CONTAINS[c] %@", filmsimlutation)
        predicates.append(simulationPredicate)
    }

    // 添加基于tags的谓词
    if let tags = tags, !tags.isEmpty {
        let tagsPredicate = NSPredicate(format: "tagsSelectAttrid CONTAINS[c] %@", tags)
        predicates.append(tagsPredicate)
    }

    // 将多个谓词组合成一个复合谓词
    let compoundPredicate = NSCompoundPredicate(andPredicateWithSubpredicates: predicates)

    // 使用复合谓词进行查询
    // 省略查询相关代码...
}