【发布时间】:2019-02-11 13:43:04
【问题描述】:
我是核心数据的新手。我有一个使用核心数据作为本地存储的应用程序。写入和读取核心数据由后台线程完成。虽然这通常有效,但在极少数情况下获取的数据是错误的,即获取的实体的属性是nil。
为了检查这种情况,我编写了一个启动 2 个异步线程的单元测试:一个从核心数据中连续获取,另一个通过首先删除所有数据,然后存储新数据来连续覆盖这些数据。
这个测试很快就引发了错误,但我不知道为什么。当然我猜这是一个多线程问题,但我不明白为什么,因为获取和删除+写入是在单个 persistentContainer 的单独托管上下文中完成的。
很抱歉下面的代码很长,虽然缩短了,但我认为没有它就无法识别问题。
非常欢迎任何帮助!
这是我获取数据的函数:
func fetchShoppingItems(completion: @escaping (Set<ShoppingItem>?, Error?) -> Void) {
persistentContainer.performBackgroundTask { (managedContext) in
let fetchRequest: NSFetchRequest<CDShoppingItem> = CDShoppingItem.fetchRequest()
do {
let cdShoppingItems: [CDShoppingItem] = try managedContext.fetch(fetchRequest)
for nextCdShoppingItem in cdShoppingItems {
nextCdShoppingItem.managedObjectContext!.performAndWait {
let nextname = nextCdShoppingItem.name! // Here, sometimes name is nil
} // performAndWait
} // for all cdShoppingItems
completion(nil, nil)
return
} catch let error as NSError {
// error handling
completion(nil, error)
return
} // fetch error
} // performBackgroundTask
} // fetchShoppingItems
我已经评论了有时会使测试崩溃的行,因为name 是nil。
这是我存储数据的函数:
func overwriteCD(shoppingItems: Set<ShoppingItem>,completion: @escaping () -> Void) {
persistentContainer.performBackgroundTask { (managedContext) in
self.deleteAllCDRecords(managedContext: managedContext, in: "CDShoppingItem")
let cdShoppingItemEntity = NSEntityDescription.entity(forEntityName: "CDShoppingItem",in: managedContext)!
for nextShoppingItem in shoppingItems {
let nextCdShoppingItem = CDShoppingItem(entity: cdShoppingItemEntity,insertInto: managedContext)
nextCdShoppingItem.name = nextShoppingItem.name
} // for all shopping items
self.saveManagedContext(managedContext: managedContext)
completion()
} // performBackgroundTask
} // overwriteCD
func deleteAllCDRecords(managedContext: NSManagedObjectContext, in entity: String) {
let deleteFetch = NSFetchRequest<NSFetchRequestResult>(entityName: entity)
let deleteRequest = NSBatchDeleteRequest(fetchRequest: deleteFetch)
deleteRequest.resultType = .resultTypeObjectIDs
do {
let result = try managedContext.execute(deleteRequest) as? NSBatchDeleteResult
let objectIDArray = result?.result as? [NSManagedObjectID]
let changes = [NSDeletedObjectsKey: objectIDArray]
NSManagedObjectContext.mergeChanges(fromRemoteContextSave: changes as [AnyHashable: Any], into: [managedContext])
} catch let error as NSError {
// error handling
}
} // deleteAllCDRecords
func saveManagedContext(managedContext: NSManagedObjectContext) {
if !managedContext.hasChanges { return }
do {
try managedContext.save()
} catch let error as NSError {
// error handling
}
} // saveManagedContext
【问题讨论】:
标签: swift multithreading core-data background-process