经过进一步的研究,并针对关于这个问题太长的评论,我重新设计了这个问题。
我正在使用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%。我很难理解在什么情况下,以这种方式添加到管理对象上下文的对象根本不会出现在插入对象列表中,也不会进入保存的数据库。一次性插入的记录数量有没有一个实际的限制?
有谁能建议我还应该在哪里找找看--这个错误很小,看起来程序运行得很好,但其实不是。
非常感谢。
我想我已经找到了问题所在,如果我是正确的,把我的解决方法贴在这里,也许能帮助到其他人。我正在使用Combine和dataTaskPublisher下载三个CSV文件--创建三个单独的发布器,然后将它们合并到一个单一的流中。虽然我知道不能在后台线程上更新UI,所以已经在链中添加了.receive(on: DispatchQueue.main)(在下面的代码中用(1)表示),但我把它放在了解码发生的.tryMap { }之后。因为是解码过程将对象插入到管理对象上下文中,这一定是异步发生的,因此导致了问题。通过将.receive(on:...)行移到.tryMap之上(见下面(2)),这似乎已经解决了问题--或者说至少使获取、解码和插入的记录返回正确的数量是一致的。
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)