Ios NSFetchedResultsController错误:尝试从节0中删除项,该节在使用userInfo(null)进行更新之前仅包含1项
我有一个使用核心数据的应用程序。里面有帖子,每个帖子都有很多标签。每个标签都有许多帖子 我有一个主视图控制器,它显示所有标记的集合视图。此collectionView的数据源由NSFetchedResultsController提供电源Ios NSFetchedResultsController错误:尝试从节0中删除项,该节在使用userInfo(null)进行更新之前仅包含1项,ios,swift,core-data,nsfetchedresultscontroller,Ios,Swift,Core Data,Nsfetchedresultscontroller,我有一个使用核心数据的应用程序。里面有帖子,每个帖子都有很多标签。每个标签都有许多帖子 我有一个主视图控制器,它显示所有标记的集合视图。此collectionView的数据源由NSFetchedResultsController提供电源 class HomeViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource,NSFetchedResultsControllerDelegate
class HomeViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource,NSFetchedResultsControllerDelegate {
@IBOutlet weak var latestTagsCollectionView: UICollectionView!
var fetchedResultsController: NSFetchedResultsController<Tag>!
var blockOperations: [BlockOperation] = []
override func viewDidLoad() {
super.viewDidLoad()
self.latestTagsCollectionView.dataSource = self
self.latestTagsCollectionView.delegate = self
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
//1
guard let appDelegate =
UIApplication.shared.delegate as? AppDelegate else {
return
}
let managedContext =
appDelegate.persistentContainer.viewContext
//2
let fetchRequest =
NSFetchRequest<NSManagedObject>(entityName: "Tag")
//3
fetchRequest.sortDescriptors = []
fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: managedContext, sectionNameKeyPath: nil, cacheName: nil) as? NSFetchedResultsController<Tag>
fetchedResultsController.delegate = self
do {
try fetchedResultsController.performFetch()
} catch let error as NSError {
print("Could not fetch. \(error), \(error.userInfo)")
}
}
func configure(cell: UICollectionViewCell, for indexPath: IndexPath) {
guard let cell = cell as? TagCollectionViewCell else {
return
}
print(indexPath,"indexPath")
let tag = fetchedResultsController.object(at: indexPath)
guard let timeAgo = tag.mostRecentUpdate as Date? else { return }
cell.timeAgo.text = dateFormatter.string(from: timeAgo)
if let imageData = tag.mostRecentThumbnail {
cell.thumbnail.image = UIImage(data:imageData as Data,scale:1.0)
} else {
cell.thumbnail.image = nil
}
cell.tagName.text = tag.name
cell.backgroundColor = UIColor.gray
}
//CollectionView Stuff
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
guard let sectionData = fetchedResultsController.sections?[section] else {
return 0
}
return sectionData.numberOfObjects
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = latestTagsCollectionView.dequeueReusableCell(withReuseIdentifier: "latestTagCell", for: indexPath) as! TagCollectionViewCell
configure(cell: cell, for: indexPath)
return cell
}
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>,
didChange anObject: Any,
at indexPath: IndexPath?,
for type: NSFetchedResultsChangeType,
newIndexPath: IndexPath?){
switch type {
case .insert:
print("Insert Object: \(String(describing: newIndexPath))")
blockOperations.append(
BlockOperation(block: { [weak self] in
if let this = self {
this.latestTagsCollectionView!.insertItems(at: [newIndexPath!])
}
})
)
case .update:
blockOperations.append(
BlockOperation(block: { [weak self] in
if let this = self {
this.latestTagsCollectionView!.reloadItems(at: [newIndexPath!])
}
})
)
case .move:
blockOperations.append(
BlockOperation(block: { [weak self] in
if let this = self {
this.latestTagsCollectionView!.moveItem(at: indexPath!, to: newIndexPath!)
}
})
)
case .delete:
print("deleted record")
blockOperations.append(
BlockOperation(block: { [weak self] in
if let this = self {
this.latestTagsCollectionView!.deleteItems(at: [newIndexPath!])
}
})
)
default: break
}
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
latestTagsCollectionView!.performBatchUpdates({ () -> Void in
for operation: BlockOperation in self.blockOperations {
operation.start()
}
}, completion: { (finished) -> Void in
self.blockOperations.removeAll(keepingCapacity: false)
})
}
deinit {
// Cancel all block operations when VC deallocates
for operation: BlockOperation in blockOperations {
operation.cancel()
}
blockOperations.removeAll(keepingCapacity: false)
}
将.insert大小写应用于NSFetchedResultsController的回调方法时打印
然后我添加了一个带有两个标签的帖子:“一”、“二”
为什么试图在此处删除项目1。。。我真的不明白这个错误消息。我认为它应该只是在新的索引路径插入一个项,因为在数据库中创建了一个新的标记“Two”。这是怎么回事 您的问题是由于在
.update
案例中使用newindepath
而不是indepath
造成的
将现有标记指定给帖子时,该标记
对象将更新。这会导致将.updated
事件发布到NSFetchResultsControllerDelegate
在委托方法中,newindepath
表示处理插入后对象的索引路径,而indepath
表示插入前对象的索引路径
各国的文件:
在批处理操作中,删除是在插入之前处理的。这意味着删除的索引是相对于批处理操作之前集合视图状态的索引进行处理的,插入的索引是相对于批处理操作中所有删除之后状态的索引进行处理的
由于插入是最后执行的,当您尝试重新加载newindepath
时会出现异常,因为您尝试重新加载尚未插入的元素
在本例中,将代码更改为引用indepath
,可以解决问题
case .update:
blockOperations.append(
BlockOperation(block: { [weak self] in
if let this = self {
this.latestTagsCollectionView!.reloadItems(at: [indexPath!])
}
})
)
此外,您只需更新
Post
或标记
;由于存在反向参考,核心数据将负责更新其他对象您的模型中是否设置了正确的反向关系?如果是这样,您应该将标记添加到post或将post添加到标记中,但不能同时添加两者。@Paulw11标记实体与目标post和反向“标记”有一种称为post的关系。Post实体与目标标记和反向“Post”有一种称为标记的关系。我读到这是你如何建立一个多对多的关系,尽管反过来有点混乱。我如何知道是将标签添加到帖子还是将帖子添加到标签?在这个例子中,我用这些标签创建了一个帖子,所以我假设只对帖子添加标签……不管你用哪种方式做,只做一个。我会在新帖子上设置标签,但无论如何都不会删除错误。该行为表明,它可能会用一个新名称(“两个”)覆盖数据库中的原始标记。我意识到,如果我在第二篇文章中使用“二”而不是“一”,“二”,这个问题就不会出现……我错了。删除数据库并进行更改确实解决了这个问题—您是说indexPath!而不是IndexPath?这个问题已经解决了,这个改变,以及您关于只将标签添加到帖子中,而不将帖子添加到标签中的建议。
Insert Object: Optional([0, 0])
Insert Object: Optional([0, 0])
2018-12-05 12:51:16.947569-0800 SweatNetOffline[71327:19904799] *** Assertion failure in -[UICollectionView _endItemAnimationsWithInvalidationContext:tentativelyForReordering:animator:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKitCore_Sim/UIKit-3698.84.15/UICollectionView.m:5908
2018-12-05 12:51:16.949957-0800 SweatNetOffline[71327:19904799] [error] fault: Serious application error. An exception was caught from the delegate of NSFetchedResultsController during a call to -controllerDidChangeContent:. attempt to delete item 1 from section 0 which only contains 1 items before the update with userInfo (null)
case .update:
blockOperations.append(
BlockOperation(block: { [weak self] in
if let this = self {
this.latestTagsCollectionView!.reloadItems(at: [indexPath!])
}
})
)