【发布时间】:2020-11-20 13:32:03
【问题描述】:
在我的GitHub project 中,我将 Codeable 与 CoreData 一起使用,因此我不需要中介结构来转换数据。我从文件中获取数据,并将它们转储到 CoreData。但是,每当我运行我的项目时,我从原始文件中使用的项目数与我从 CoreData 获取的项目总数不同。而且每次构建获取的项目数量都不同。
我想在我的核心数据中保存 1000 个项目。但是,当我构建我的项目时,至少有 5 次有 1 次,我发现我的项目少于 1000 个。我检查了我从后端获取的数据,我也在检查我从 Core Data 获取的数据,但数字不匹配。
在我的 viewDidLoad 上,我调用了 retrieveData()
guard let url = URL(string: <URL here>)
else { return }
let task = URLSession.shared.dataTask(
with: url as URL,
completionHandler: { [weak self] (data, response, error) in
guard let dataResponse = data, error == nil else {
print("error!!!")
return
}
self?.decodeAndSaveAndFetch(dataResponse: dataResponse)
}
)
task.resume()
}
这是我的 decodeAndSaveAndFetch(),我从服务调用中解码 json,将项目保存在我的 CoreData 中并获取它以显示:
func decodeAndSaveAndFetch(dataResponse: Data) {
let decoder = JSONDecoder()
guard let codingUserInfoKeyContext = CodingUserInfoKey.context else { return }
decoder.userInfo[codingUserInfoKeyContext] = container?.viewContext
container?.performBackgroundTask { [weak self] context in
do {
let filePath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
guard let fileName = filePath?.appendingPathComponent("COREDATA.txt") else { return }
try dataResponse.write(to: fileName)
_ = try decoder.decode([Ingredient].self, from: dataResponse)
try context.save()
let request: NSFetchRequest<Ingredient> = Ingredient.fetchRequest()
do {
try print(self?.container?.viewContext.count(for: request))
self?.data = try self?.container?.viewContext.fetch(request)
DispatchQueue.main.async { [weak self] in
self?.tableView.reloadData()
}
} catch {
print("fetch failed")
}
} catch {
print("decode and save failed")
}
}
}
在 COREDATA.txt 中,我总是得到 1000 个项目,但是当我打印请求的计数时,有时得到的项目少于 1000 个。
这是我的带有 fetch 方法的成分类:
extension CodingUserInfoKey {
static let context = CodingUserInfoKey(rawValue: "context")
}
@objc(Ingredient)
public class Ingredient: NSManagedObject, Codable {
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
do {
try container.encode(id ?? "blank", forKey: .id)
} catch {
print("error")
}
}
required convenience public init(from decoder: Decoder) throws {
// return the context from the decoder userinfo dictionary
guard let contextUserInfoKey = CodingUserInfoKey.context,
let managedObjectContext = decoder.userInfo[contextUserInfoKey] as? NSManagedObjectContext,
let entity = NSEntityDescription.entity(forEntityName: "Ingredient", in: managedObjectContext)
else {
fatalError("decode failure")
}
// Super init of the NSManagedObject
self.init(entity: entity, insertInto: managedObjectContext)
let values = try decoder.container(keyedBy: CodingKeys.self)
do {
id = try values.decode(String.self, forKey: .id)
desc = try values.decode(String.self, forKey: .desc)
ingredientType = try values.decode(Set<IngredientType>.self, forKey: .ingredientType)
} catch {
print ("error")
}
}
enum CodingKeys: String, CodingKey {
case id = "id"
case desc = "desc"
case ingredientType = "ingredientType"
}
}
extension Ingredient {
@nonobjc public class func fetchRequest() -> NSFetchRequest<Ingredient> {
return NSFetchRequest<Ingredient>(entityName: "Ingredient")
}
@NSManaged public var id: String?
@NSManaged public var desc: String?
@NSManaged public var ingredientType: Set<IngredientType>?
}
请指教。谢谢!
编辑:Link to GitHub project 重现:在 XCode 中构建和运行 - 观察计数。
【问题讨论】:
-
不相关但从不在
catch块中打印无意义的文字字符串。打印实际的error。 -
您能否确认您在 SQLite 属性中使用的关系是什么?有时,如果您在关系中选择多对多而不是一对一或类似的事情,那么这可能会发生。
-
@ParthTamane,没错,成分与成分类型具有多对多关系。成分可以有多种类型,每种类型都属于多个成分。如何解决?
-
尝试一对多。因为
Ingredient是您的基本属性。我认为这应该可行。 -
@ParthTamane 不幸的是,它没有用。我什至删除了反比关系,但问题仍然间歇性地发生。