Xcode14中CoreData重量级迁移后无法保存数据求助
CoreData重量级迁移后无法存储数据,查询返回空结果问题
问题描述
项目功能升级完成CoreData重量级迁移后,出现以下异常:
- 调用插入方法后,
managedObject.isUpdated始终输出false,控制台打印“Success insert”但数据未持久化 - 重启应用后数据丢失,查询方法返回空数组
- 回滚到迁移前的Git分支,数据保存与查询功能恢复正常
相关代码
插入方法
class func insert(object: [String: Any], entityName: String) -> NSManagedObject? { let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext let entity = NSEntityDescription.entity(forEntityName: entityName, in: context)! let managedObject = NSManagedObject(entity: entity, insertInto: context) let keys = Array(entity.attributesByName.keys) print("Keys:\(keys)\nobject:\(object)") for (key, element) in object { if keys.contains(key) { managedObject.setValue(element, forKey: key) } } do { print(managedObject.isUpdated) try context.save() print(managedObject.isUpdated) print("Success insert") } catch { print("Error in Insert: \(error)") } return managedObject }
查询方法
class func fetchList(entityName: String, predicate: NSPredicate? = nil, sortBy: [(key: String, ascending: Bool)]? = nil) -> [NSManagedObject]? { let context = CoreDataStack.context let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: entityName) fetchRequest.predicate = predicate if let sortedBy = sortBy { var sortDescriptors = [NSSortDescriptor]() for sortElement in sortedBy { let sortDescriptor = NSSortDescriptor(key: sortElement.key, ascending: sortElement.ascending) sortDescriptors.append(sortDescriptor) } fetchRequest.sortDescriptors = sortDescriptors } do { let results = try context.fetch(fetchRequest) return results } catch { print("Could not fetch: \(error)") } return nil }
控制台日志(隐私信息已打码)
Keys:["contactIdentifier", "timeAndDate", "receiverId", "createdAt", "updatedAt", "i-----------g", "se---d", "duration", "senderId", "number", "name", "isFromiOS", "opponentsIds", "m-------d"] object:["sessionID": "E1ECAB27-D312", "message": "--------", "contactIdentifier": "------- ", "I----p": "1", "contactNumbers": "------------,-----------", "V-----l": "1", "co------pe": "2", "createdAt": 2022-10-27 11:58:18 +0000, "opponentsIds": "---, ----"] false false Success insert updateCallInformation:<CallHistory2: 0x2835e99f0> (entity: CallHistory2; id: 0xa46b7d533bd5d14b <x-coredata://BAF5AFD6-8567-4485-8938-E13D3BD7B314/CallHistory2/p1>; data: { contactIdentifier = "MDanishPK "; createdAt = "2022-10-27 11:58:18 +0000"; duration = nil; incomingOutgoing = nil; isFromiOS = 0; missedReceived = nil; name = nil; number = nil; opponentsIds = nil; receiverId = nil; senderId = nil; sessionID = "E1ECAB27-D312-4402-91D3-87AEBAC1A709"; timeAndDate = nil; updatedAt = nil; })
排查与解决步骤
1. 统一上下文实例
插入方法使用AppDelegate的persistentContainer.viewContext,但查询方法用CoreDataStack.context,两个上下文可能不属于同一个NSPersistentContainer:
- 检查
CoreDataStack.context的初始化逻辑,确认它和AppDelegate中的容器是同一个实例 - 全局使用单例
NSPersistentContainer,避免多上下文实例导致数据不同步
2. 核对实体属性匹配
从日志可见,传入的opponentsIds在托管对象中显示为nil,说明属性赋值未生效:
- 检查属性名大小写:CoreData属性名大小写敏感,比如传入的
sessionID不在实体Keys列表中,但托管对象却有该值,需确认实体模型是否正确配置 - 验证迁移映射模型:确保重量级迁移的映射模型中,新旧实体的属性映射关系正确,无遗漏或错误
- 检查属性可选性与类型:如果属性设为非可选但传入值不匹配,CoreData可能静默忽略赋值操作
3. 确认上下文变更状态
managedObject.isUpdated始终为false,说明对象未被标记为已修改:
- 替换
setValue(_:forKey:)为直接属性访问(若有生成托管对象子类),避免键值编码的潜在问题 - 保存前调用
context.hasChanges确认是否有未提交的变更 - 检查实体属性的
isReadOnly设置,确保属性可写
4. 验证持久化存储路径
迁移可能导致存储路径错误,数据写入临时位置导致重启丢失:
- 打印
persistentContainer.persistentStoreCoordinator.persistentStores中的url,确认存储路径指向应用Documents目录 - 检查存储文件是否存在,以及文件权限是否正常
5. 启用CoreData调试日志
添加启动参数-com.apple.CoreData.SQLDebug 1,查看SQL执行细节:
- 确认插入语句是否正确生成并执行
- 检查查询语句的表名、字段是否与实体模型匹配
6. 捕获迁移过程错误
在NSPersistentContainer加载存储时,捕获所有错误,确保迁移无隐性失败:
persistentContainer.loadPersistentStores(completionHandler: { (storeDescription, error) in if let error = error as NSError? { fatalError("Unresolved error \(error), \(error.userInfo)") } })
内容的提问来源于stack exchange,提问作者Muhammad Danish Qureshi
相关产品推荐
相关产品推荐

