【问题标题】:Swift CoreData - objects not being inserted in to managed object contextSwift CoreData - 未插入托管对象上下文的对象
【发布时间】:2020-05-31 22:07:04
【问题描述】:

在进一步研究后,我重新设计了这个问题,并回应 cmets 它太长了。

我正在使用 CodableCSV 从三个 URL 下载和解码 CSV 格式的数据,并且我已经能够确认我收到了我期望的所有数据(截至今天,35027 行)。当数据被解码时,我将一个 NSManagedObjectContext 注入到解码的对象中。这是我的托管对象类:

import Foundation
import CoreData

@objc(MacListEntry)
class MacListEntry: NSManagedObject, Decodable {

  //var id = UUID()
  @NSManaged var registry: String?
  @NSManaged var assignment: String?
  @NSManaged var org_name: String?
  @NSManaged var org_addr: String?

  required convenience init(from decoder: Decoder) throws {

    guard let keyManObjContext = CodingUserInfoKey.managedObjectContext,
      let context = decoder.userInfo[keyManObjContext] as? NSManagedObjectContext,
      let entity = NSEntityDescription.entity(forEntityName: "MacListEntry", in: context) else {
        fatalError("Failed to receive managed object context")
    }

    self.init(entity: entity, insertInto: context)

    let container = try decoder.container(keyedBy: CodingKeys.self)
    self.registry = try container.decode(String.self, forKey: .registry)
    self.assignment = try container.decode(String.self, forKey: .assignment)
    self.org_name = try container.decode(String.self, forKey: .org_name)
    self.org_addr = try container.decode(String.self, forKey: .org_addr)
  }

  private enum CodingKeys: Int, CodingKey {
    case registry = 0
    case assignment = 1
    case org_name = 2
    case org_addr = 3
  }
}

public extension CodingUserInfoKey {
  static let managedObjectContext = CodingUserInfoKey(rawValue: "managedObjectContext")
}

然后我尝试使用try context.save() 保存上下文,但在此之前,请检查我尝试使用以下方法插入的记录数:

print("Deleted objects: (self.persistentContainer.viewContext.deletedObjects.count)")
print("Inserted objects: (self.persistentContainer.viewContext.insertedObjects.count)")
print("Has changes: \(self.persistentContainer.viewContext.hasChanges)")

并在每次代码运行时获得不同数量的插入记录 - 总是很短,大约 0.5%。我很难理解在什么情况下以这种方式添加到托管对象上下文的对象不会出现在插入对象列表中,也不会出现在保存的数据库中。一次插入的记录数有实际限制吗?

任何人都可以建议我应该在哪里寻找 - 错误非常小,看起来程序运行良好,但事实并非如此。

非常感谢。

【问题讨论】:

  • 在寻求调试帮助时,您的想法是您提供一个minimally reproducible example 以将问题归零。通常情况下,您要么暴露了理解上的差距,要么发现了错误。我怀疑你会让任何人解析所有这些代码。

标签: swift core-data nsurlrequest combine


【解决方案1】:

我想我已经找到了问题所在,如果我是正确的,在这里发布我的解决方案可能会对其他人有所帮助。我正在使用 Combine 和 dataTaskPublisher 下载三个 CSV 文件 - 创建三个单独的发布者,然后将它们合并到一个流中。虽然我知道我无法在后台线程上更新 UI,所以在链中添加了 .receive(on: DispatchQueue.main) (在下面的代码中由 (1) 表示),我把它放在了 .receive(on: DispatchQueue.main) 之后。 tryMap { } 发生解码的地方。因为正是解码过程将对象插入到托管对象上下文中,所以这一定是异步发生的,因此会导致问题。通过将 .receive(on: ...) 行移到 .tryMap 上方(参见下面的 (2)),这似乎解决了问题 - 或者至少使 fetch、decode 和 insert 返回正确数量的插入记录始终如一。

enum RequestError: Error {
    case sessionError(error: HTTPURLResponse)
}


struct Agent {

  let decoder: CSVDecoder

  struct Response<T> {
    let value: T
    let response: URLResponse
  }
  func run<T: Decodable>(_ request: URLRequest) -> AnyPublisher<Response<T>, Error> {
    print("In run()")
    return URLSession.shared
      .dataTaskPublisher(for: request)
      .receive(on: DispatchQueue.main)    // -- (1)
      .tryMap { result -> Response<T> in
        print(result)

        guard let httpResponse = result.response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
          throw RequestError.sessionError(error: result.response as! HTTPURLResponse)
        }
        let value = try self.decoder.decode(T.self, from: result.data)
        return Response(value: value, response: result.response)
    }
    //.receive(on: DispatchQueue.main).    // -- (2)
    .eraseToAnyPublisher()
  }
}

struct Endpoint {
  var agent: Agent
  let base: String
}

extension Endpoint {

  func run<T: Decodable>(_ request: URLRequest) -> AnyPublisher<T, Error> {
    return agent.run(request)
      .map(\.value)
      .eraseToAnyPublisher()
  }

  func fetch(thing: String) -> AnyPublisher<[MacListEntry], Error> {
    return run(URLRequest(url: URL(string: base+thing)!))
  }

}

struct Response: Codable {
  let name: String
}

--- snip ---

    var requests:[AnyPublisher<[MacListEntry],Error>] = []
    requests.append(endpoint.fetch(thing: "file1.csv"))
    requests.append(endpoint.fetch(thing: "file2.csv"))
    requests.append(endpoint.fetch(thing: "file3.csv"))


    let _ = Publishers.MergeMany(requests)
      .sink(receiveCompletion: { completion in
        ...
      )
      .store(in: &bag)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-23
    • 2021-11-14
    • 1970-01-01
    相关资源
    最近更新 更多