【问题标题】:iOS: Data getting lost in Core DataiOS:数据在 Core Data 中丢失
【发布时间】: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 不幸的是,它没有用。我什至删除了反比关系,但问题仍然间歇性地发生。

标签: ios swift core-data


【解决方案1】:

尝试将方向从多个Ingredient 更改为一个IngredientType。我认为绝对不应该是多对多的。在列出待办事项时,我遇到了类似的问题。这种关联方法允许将一组固定项目类型中的一个项目类型与多个项目相关联。

查看这篇 StackOverflow 帖子,了解如何拥有一组项目类型:Array of Objects in Core Data Entity?。他们建议将To Many 用于toItemType 关系。

这就是我建立关系的方式:

这些项目是这样访问的:

func configureCell(item: Item) {
        
    title.text = item.title
    price.text = "$\(item.price)"
    details.text = item.details
    type.text = item.toItemType?.type
    thumb.image = item.toImage?.image as? UIImage
}

【讨论】:

  • 如果您的 TODO 项目有多种类型,您如何显示这些数据?例如,TODO: Jogging with friends 的类型为 SOCIALEXERCISE
  • @IsiahMalit 好问题。我认为你将不得不设置一个项目列表。这种关系的作用是允许多个待办事项与相同的固定类型的项目相关联,例如SOCIAL。看看上面是如何访问它的。
  • 你好帕思!我阅读了您提供的链接。这和我拥有的没有什么不同。在我最初的帖子中,我发布了具有 @NSManaged public var ingredientType: Set&lt;IngredientType&gt;? 的成分类。我已经有一个to-many 用于Type。不幸的是,您的与我的相反,提供的链接对我的模型并不适用。我尝试了Ingredient &lt;&lt;---&gt;&gt; IngredientTypeIngredient &lt;--&gt;&gt; IngredientType,但都没有成功。 Ingredient &lt;&lt;---&gt; IngredientType 不起作用,因为每种成分都有多种成分类型。
  • 我认为您不需要手动定义一个集合,但我可能是错的。根据那个链接,只要你有多对一的关系,它应该让你添加多个,对吧?它说您需要在获得该选项之前进行重建。另外,如果您的代码在 GitHub 上,那么我可以克隆并在周末查看它。那样可能会更快。
  • 你好帕思!这是 GitHub 的链接:github.com/imalit/TestNestedCoreData。我将 Codeable 与 CoreData 一起使用,因此我不需要中介结构来转换数据。我最初使用的是 Firebase 中的数据,但由于我想将数据保密,因此我创建了一个 json 文件。结果是一样的:我期待 1000 件商品,但得到的却更少。
猜你喜欢
  • 1970-01-01
  • 2018-11-10
  • 2018-05-09
  • 2014-11-03
  • 1970-01-01
  • 2012-09-30
  • 1970-01-01
  • 2015-01-05
  • 1970-01-01
相关资源
最近更新 更多