Core Data中使用ValueTransformer保存数组数据重启后丢失问题排查
问题:Core Data中使用ValueTransformer保存UUID数组,重启后数据丢失
配置ValueTransformer保留数组对象,创建数据后刷新可正常查看,但关闭重启程序后,habitData数组数据丢失,其余数据完好,重启后habitData始终返回nil。
相关代码
启动代码
func createDatabase() { ValueTransformer.setValueTransformer(UUIDTransformer(), forName: NSValueTransformerName("UUIDTransformer")) ValueTransformer.setValueTransformer(TheCategoriesTransformer(), forName: NSValueTransformerName("TheCategoriesTransformer")) do { theDailyData = try contextDataCore.fetch(The_daily_data.fetchRequest()) } catch { } do { theHabitList = try contextDataCore.fetch(The_habit_list.fetchRequest()) } catch { } do { thePhotoData = try contextDataCore.fetch(The_photos.fetchRequest()) } catch { } }
转换器代码
class UUIDTransformer: ValueTransformer { override func transformedValue(_ value: Any?) -> Any? { guard let uuid = value as? [UUID] else { return nil } do { let data = try NSKeyedArchiver.archivedData(withRootObject: uuid, requiringSecureCoding: true) return data } catch { return nil } } override func reverseTransformedValue(_ value: Any?) -> Any? { guard let data = value as? Data else { return nil } do { let uuid = try NSKeyedUnarchiver.unarchivedObject(ofClass: NSArray.self, from: data) as! [UUID] return uuid } catch { return nil } } } class TheCategoriesTransformer: ValueTransformer { override func transformedValue(_ value: Any?) -> Any? { guard let uuid = value as? [String] else { return nil } do { let data = try NSKeyedArchiver.archivedData(withRootObject: uuid, requiringSecureCoding: true) return data } catch { return nil } } override func reverseTransformedValue(_ value: Any?) -> Any? { guard let data = value as? Data else { return nil } do { let categories = try NSKeyedUnarchiver.unarchivedObject(ofClass: NSArray.self, from: data) as! [String] return categories } catch { return nil } } }
创建数据代码
func newJournalData(theDate: Date, theHabitID: UUID? = nil, theSubject: String? = nil, theJournal: String? = nil, theMind: String? = nil, theWeather: String = "NA") { let repairDate = Calendar.current.dateComponents([.year, .month, .day], from: theDate) let freshDate = Calendar.current.date(from: repairDate) var newHabitID: [UUID] = [] if theHabitID != nil { newHabitID.append(theHabitID!) } var theImpactCount = 0 var theImpactTotal = 0 let findHabitImpact = theHabitList.first(where: { $0.theID == theHabitID }) for countX in theHabitList { switch countX.impact { case 0: theImpactTotal += 1 case 1: theImpactTotal += 5 case 2: theImpactTotal += 10 default: theImpactTotal += 0 } } if findHabitImpact != nil { switch findHabitImpact?.impact { case 0: theImpactCount += 1 case 1: theImpactCount += 5 case 2: theImpactCount += 10 default: theImpactCount += 0 } } let finalImpact = Int((Double(theImpactCount) / Double(theImpactTotal)) * 100) let newJournalData = The_daily_data(context: contextDataCore) newJournalData.date = freshDate newJournalData.habitData = newHabitID newJournalData.theSubject = theSubject newJournalData.journal = theJournal newJournalData.yourMind = theMind newJournalData.theWeather = theWeather newJournalData.percent = Int16(finalImpact) do { try contextDataCore.save(); createDatabase() } catch { print("Error") } }
问题原因及解决方法
核心问题
UUIDTransformer的反向转换方法中,仅用NSArray.self作为解归档的类,无法验证数组内UUID元素的安全编码类型,导致程序重启后解归档失败,返回nil。
修复步骤
修改UUIDTransformer的反向转换方法
使用ofClasses方法同时指定数组和元素的类型,确保UUID能被正确解析:override func reverseTransformedValue(_ value: Any?) -> Any? { guard let data = value as? Data else { return nil } do { let uuidArray = try NSKeyedUnarchiver.unarchivedObject(ofClasses: [NSArray.self, UUID.self], from: data) as? [UUID] return uuidArray } catch { print("UUID数组解归档失败: \(error)") return nil } }调整转换器注册时机
必须在Core Data上下文初始化之前注册转换器,建议把注册代码放到AppDelegate的启动方法或Core Data栈初始化代码中,避免上下文创建后注册导致转换器未生效。验证Core Data模型配置
检查The_daily_data实体的habitData属性:- 类型设为
Transformable Value Transformer Name填写UUIDTransformer(与代码中注册的名称完全一致)- 勾选
Allows External Storage(优化大数组存储)
- 类型设为
额外优化建议
- 避免使用强制解包
as!,改用可选绑定as?防止崩溃 - 对
TheCategoriesTransformer做同样修改,用[NSArray.self, NSString.self]指定类型提升稳定性:override func reverseTransformedValue(_ value: Any?) -> Any? { guard let data = value as? Data else { return nil } do { let categories = try NSKeyedUnarchiver.unarchivedObject(ofClasses: [NSArray.self, NSString.self], from: data) as? [String] return categories } catch { print("分类数组解归档失败: \(error)") return nil } }
内容的提问来源于stack exchange,提问作者Travgalax
相关产品推荐
相关产品推荐

