Ios 在后台上下文中保存NSManagedObjects时出现问题
我已经为此挣扎了好几天了。我将感谢任何帮助 我有一个位置Ios 在后台上下文中保存NSManagedObjects时出现问题,ios,swift,multithreading,core-data,data-persistence,Ios,Swift,Multithreading,Core Data,Data Persistence,我已经为此挣扎了好几天了。我将感谢任何帮助 我有一个位置NSManagedObject和一个图像NSManagedObject,它们有一对多关系,即一个位置有多个图像 我有两个屏幕,在第一个屏幕中,用户在视图上下文中添加位置,添加和检索位置时不会出现问题 现在,在第二个屏幕中,我想根据在第一个屏幕中选择的位置检索图像,然后在集合视图中显示图像。图像首先从flickr检索,然后保存在数据库中 我想在背景环境中保存和检索图像,这给我带来了很多问题 当我试图保存从flickr检索到的每个图像时,我会收
NSManagedObject
和一个图像NSManagedObject
,它们有一对多关系,即一个位置有多个图像
我有两个屏幕,在第一个屏幕中,用户在视图上下文中添加位置,添加和检索位置时不会出现问题
现在,在第二个屏幕中,我想根据在第一个屏幕中选择的位置检索图像,然后在集合视图中显示图像。图像首先从flickr检索,然后保存在数据库中
我想在背景环境中保存和检索图像,这给我带来了很多问题
func saveImagesToDb () {
//Store the image in the DB along with its location on the background thread
if (doesImageExist()){
dataController.backgroundContext.perform {
for downloadedImage in self.downloadedImages {
print ("saving to context")
let imageOnMainContext = Image (context: self.dataController.viewContext)
let imageManagedObjectId = imageOnMainContext.objectID
let imageOnBackgroundContext = self.dataController.backgroundContext.object(with: imageManagedObjectId) as! Image
let locationObjectId = self.imagesLocation.objectID
let locationOnBackgroundContext = self.dataController.backgroundContext.object(with: locationObjectId) as! Location
let imageData = NSData (data: downloadedImage.jpegData(compressionQuality: 0.5)!)
imageOnBackgroundContext.image = imageData as Data
imageOnBackgroundContext.location = locationOnBackgroundContext
try? self.dataController.backgroundContext.save ()
}
}
}
}
正如您在上面的代码中所看到的,我正在基于从视图上下文中检索到的ID在后台上下文中构建NSManagedObject。每次调用saveImagesToDb
时,我都会收到警告,那么问题出在哪里
由于未捕获的异常“NSInternalInconsistencyException”而终止应用程序,原因:“无效更新:节0中的项目数无效。”。更新(4)后包含在现有节中的项目数必须等于更新(1)前包含在该节中的项目数,加上或减去从该节插入或删除的项目数(1插入,0删除),加上或减去移入或移出该节的项目数(0移入,0移出)。
'
以下是一些与设置FetchedResultsController和基于上下文或FetchedResultsController中的更改更新集合视图相关的代码片段
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
guard let imagesCount = fetchedResultsController.fetchedObjects?.count else {return 0}
return imagesCount
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
print ("cell data")
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "photoCell", for: indexPath) as! ImageCell
//cell.placeImage.image = UIImage (named: "placeholder")
let imageObject = fetchedResultsController.object(at: indexPath)
let imageData = imageObject.image
let uiImage = UIImage (data: imageData!)
cell.placeImage.image = uiImage
return cell
}
func setUpFetchedResultsController () {
print ("setting up controller")
//Build a request for the Image ManagedObject
let fetchRequest : NSFetchRequest <Image> = Image.fetchRequest()
//Fetch the images only related to the images location
let locationObjectId = self.imagesLocation.objectID
let locationOnBackgroundContext = self.dataController.backgroundContext.object(with: locationObjectId) as! Location
let predicate = NSPredicate (format: "location == %@", locationOnBackgroundContext)
fetchRequest.predicate = predicate
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "location", ascending: true)]
fetchedResultsController = NSFetchedResultsController (fetchRequest: fetchRequest, managedObjectContext: dataController.backgroundContext, sectionNameKeyPath: nil, cacheName: "\(latLongString) images")
fetchedResultsController.delegate = self
do {
try fetchedResultsController.performFetch ()
} catch {
fatalError("couldn't retrive images for the selected location")
}
}
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
print ("object info changed in fecthed controller")
switch type {
case .insert:
print ("insert")
DispatchQueue.main.async {
print ("calling section items")
self.collectionView!.numberOfItems(inSection: 0)
self.collectionView.insertItems(at: [newIndexPath!])
}
break
case .delete:
print ("delete")
DispatchQueue.main.async {
self.collectionView!.numberOfItems(inSection: 0)
self.collectionView.deleteItems(at: [indexPath!])
}
break
case .update:
print ("update")
DispatchQueue.main.async {
self.collectionView!.numberOfItems(inSection: 0)
self.collectionView.reloadItems(at: [indexPath!])
}
break
case .move:
print ("move")
DispatchQueue.main.async {
self.collectionView!.numberOfItems(inSection: 0)
self.collectionView.moveItem(at: indexPath!, to: newIndexPath!)
}
}
}
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int, for type: NSFetchedResultsChangeType) {
print ("section info changed in fecthed controller")
let indexSet = IndexSet(integer: sectionIndex)
switch type {
case .insert:
self.collectionView!.numberOfItems(inSection: 0)
collectionView.insertSections(indexSet)
break
case .delete:
self.collectionView!.numberOfItems(inSection: 0)
collectionView.deleteSections(indexSet)
case .update, .move:
fatalError("Invalid change type in controller(_:didChange:atSectionIndex:for:). Only .insert or .delete should be possible.")
}
}
func addSaveNotificationObserver() {
removeSaveNotificationObserver()
print ("context onbserver notified")
saveObserverToken = NotificationCenter.default.addObserver(forName: .NSManagedObjectContextObjectsDidChange, object: dataController?.backgroundContext, queue: nil, using: handleSaveNotification(notification:))
}
func removeSaveNotificationObserver() {
if let token = saveObserverToken {
NotificationCenter.default.removeObserver(token)
}
}
func handleSaveNotification(notification:Notification) {
DispatchQueue.main.async {
self.collectionView!.numberOfItems(inSection: 0)
self.collectionView.reloadData()
}
}
func collectionView(collectionView:UICollectionView,numberofitemsinssection:Int)->Int{
guard let ImageScont=fetchedResultsController.FetchedObject?.count else{return 0}
返回图像搜索
}
func collectionView(collectionView:UICollectionView,cellForItemAt indexPath:indexPath)->UICollectionViewCell{
打印(“单元数据”)
让cell=collectionView.dequeueReusableCell(带有reuseidentifier:“photoCell”,for:indexPath)作为!ImageCell
//cell.placeImage.image=UIImage(名为“占位符”)
让imageObject=fetchedResultsController.object(位于:indexPath)
让imageData=imageObject.image
让uiImage=uiImage(数据:imageData!)
cell.placeImage.image=uiImage
返回单元
}
func setUpFetchedResultsController(){
打印(“设置控制器”)
//为映像ManagedObject生成请求
let fetchRequest:NSFetchRequest=Image.fetchRequest()
//获取仅与图像位置相关的图像
让locationObjectId=self.imagesLocation.objectID
让locationOnBackgroundContext=self.dataController.backgroundContext.object(带有:locationObjectId)作为!位置
let谓词=NSPredicate(格式:“位置==%@”,locationOnBackgroundContext)
fetchRequest.predicate=谓词
fetchRequest.sortDescriptors=[NSSortDescriptor(键:“位置”,升序:true)]
fetchedResultsController=NSFetchedResultsController(fetchRequest:fetchRequest,managedObjectContext:dataController.backgroundContext,sectionNameKeyPath:nil,cacheName:“\(latLongString)图像”)
fetchedResultsController.delegate=self
做{
请尝试fetchedResultsController.performFetch()
}抓住{
fatalError(“无法检索所选位置的图像”)
}
}
func控制器(controller:NSFetchedResultsController,didChange anObject:Any,在indexPath:indexPath?处,对于类型:NSFetchedResultsChangeType,newIndexPath:indexPath?){
打印(“对象信息在fecthed控制器中更改”)
开关类型{
案例.插入:
打印(“插入”)
DispatchQueue.main.async{
打印(“调用节项”)
self.collectionView!.numberOfItems(第0节)
self.collectionView.insertItems(位于:[newIndexPath!]
}
打破
案例.删除:
打印(“删除”)
DispatchQueue.main.async{
self.collectionView!.numberOfItems(第0节)
self.collectionView.deleteItems(位于:[indexPath!))
}
打破
案例。更新:
打印(“更新”)
DispatchQueue.main.async{
self.collectionView!.numberOfItems(第0节)
self.collectionView.reloadeItems(位于:[indexPath!))
}
打破
案件.动议:
打印(“移动”)
DispatchQueue.main.async{
self.collectionView!.numberOfItems(第0节)
self.collectionView.moveItem(位于:indepath!,至:newindepath!)
}
}
}
func控制器(控制器:NSFetchedResultsController,didChange sectionInfo:NSFetchedResultsSectionInfo,atSectionIndex sectionIndex:Int,类型:NSFetchedResultsChangeType){
打印(“在fecthed控制器中更改的节信息”)
让indexSet=indexSet(整数:sectionIndex)
开关类型{
案例.插入:
self.collectionView!.numberOfItems(第0节)
collectionView.insertSections(indexSet)
打破
案例.删除:
self.collectionView!.numberOfItems(第0节)
collectionView.deleteSecti
guard let imagesCount = fetchedResultsController.fetchedObjects?.count else {return 0}
DispatchQueue.main.async {
print ("calling section items")
self.collectionView!.numberOfItems(inSection: 0)
self.collectionView.insertItems(at: [newIndexPath!])
}
func controllerWillChangeContent(_ controller:
NSFetchedResultsController<NSFetchRequestResult>) {
DispatchQueue.main.async {
self.collectionView.reloadData()
}
}
func saveImagesToDb () {
//Store the image in the DB along with its location on the background thread
dataController.backgroundContext.perform {
for downloadedImage in self.downloadedImages {
let imageOnBackgroundContext = Image (context: self.dataController.backgroundContext)
//imagesLocation is on the view context
let locationObjectId = self.imagesLocation.objectID
let locationOnBackgroundContext = self.dataController.backgroundContext.object(with: locationObjectId) as! Location
let imageData = NSData (data: downloadedImage.jpegData(compressionQuality: 0.5)!)
imageOnBackgroundContext.image = imageData as Data
imageOnBackgroundContext.location = locationOnBackgroundContext
guard (try? self.dataController.backgroundContext.save ()) != nil else {
self.showAlert("Saving Error", "Couldn't store images in Database")
return
}
}
}
}