【发布时间】:2018-02-08 04:02:50
【问题描述】:
我在我的项目中使用 MagicalRecord,在我的数据库中我有 CDSong 实体,它可以由多个 CDVoter 实体投票。
使用从串行调度队列中调用的NSManagedObjectContext.performAndWait(block:) 在后台添加和删除选民。我有一个NSFetchedResultsController,它获取 CDSongs 并显示他们的选民(在这个简单的场景中,它只打印选民的名字)。
一切都会好的,但我偶尔会在 NSFetchedResultsControllerDelegate 的 controllerDidChangeContent 方法中收到崩溃:-/ 根据我的分析,CDSong.voters 关系中似乎出现了一些无效的空 CDVoter (name = nil, votedSong = nil) 对象。这些空选民不是从CDVoter.mr_findAll()返回的。
这是模拟崩溃的代码(通常在单击 controllerDidChangeContent 和 buttonPressed 方法中。谢谢你的帮助:)
import UIKit
import CoreData
import MagicalRecord
class MRCrashViewController : UIViewController, NSFetchedResultsControllerDelegate {
var frc: NSFetchedResultsController<NSFetchRequestResult>!
let dispatchQueue = DispatchQueue(label: "com.testQueue")
override func viewDidLoad() {
super.viewDidLoad()
self.initializeDatabase()
self.initializeFrc()
}
func initializeDatabase() {
MagicalRecord.setLoggingLevel(MagicalRecordLoggingLevel.error)
MagicalRecord.setupCoreDataStack()
MagicalRecord.setLoggingLevel(MagicalRecordLoggingLevel.warn)
if CDSong.mr_findFirst() == nil {
for i in 1...5 {
let song = CDSong.mr_createEntity()!
song.id = Int16(i)
}
}
NSManagedObjectContext.mr_default().mr_saveToPersistentStoreAndWait()
}
func initializeFrc() {
let fetchRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName: "CDSong")
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "id", ascending: true)]
NSFetchedResultsController<NSFetchRequestResult>.deleteCache(withName: nil)
self.frc = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: NSManagedObjectContext.mr_default(), sectionNameKeyPath: nil, cacheName: nil)
self.frc!.delegate = self
try! self.frc!.performFetch()
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
for song in controller.fetchedObjects! {
print((song as! CDSong).voters!.reduce("", { $0 + ($1 as! CDVoter).name! }))
}
print("----");
}
@IBAction func buttonPressed(_ sender: Any) {
for _ in 1...10 {
self.dispatchQueue.async {
let moc = NSManagedObjectContext.mr_()
moc.performAndWait {
for song in CDSong.mr_findAll(in: moc)! {
let song = song as! CDSong
let voters = song.voters!
for voter in voters {
(voter as! CDVoter).mr_deleteEntity(in: moc)
}
for _ in 1...4 {
if arc4random()%2 == 0 {
let voter = CDVoter.mr_createEntity(in: moc)!
voter.name = String(UnicodeScalar(UInt8(arc4random()%26+65)))
voter.votedSong = song
}
}
}
moc.mr_saveToPersistentStoreAndWait()
}
}
}
}
}
注意: 我尝试使用 MagicalRecord.save(blockAndWait:) 没有成功。
【问题讨论】:
-
"选民在后台使用串行调度队列添加和删除" .对于核心数据,您不应该使用自己的队列。您应该仅使用 NSManagedObjectContext PerformBlock 方法。
-
@Sneak 我使用 performBlockAndWait 方法来处理核心数据,但我从 bg 队列中调用它们。我认为这没关系,至少docs 没有说我只能从主线程调用 performBlockAndWait,他们只说我应该从创建上下文的同一个线程调用该方法。我必须使用 bg 队列,因为如果从 main 调用 performBlockAndWait 会阻塞图形,并且仅调用 perform 会混合删除和插入(相信我,刚刚尝试过)。谢谢,我会修改句子
-
删除了可以解决您问题的我的 cmets,而是输入此内容。既然你对你的“句子编辑”和“相信我”如此自大,而不是质疑我的观点并试图学习一些东西。祝你好运。
-
@Sneak 哦,伙计,对不起,如果听起来像那样,我不想争论,我质疑你的观点,研究了他们周围的文档并测试了可能的解决方案,这些解决方案只调用 performBlock 方法从主队列(我用“相信我”来表达我不只是拒绝可能性,而是真的尝试了它们)。而且我认为在stackoverflow中编辑误导性的句子是正常的甚至是期望的事情,不是吗?但是,我会根据您删除的 cmets 尝试一些新的想法,并让您知道,如果它们有效......
-
不用担心,如果你想避免阻塞你的 UI,你应该用 NSPrivateQueueConcurrencyType 创建一个 NSManagedObjectContext ,然后做你的工作。您可以创建一个子上下文,在保存时将更改推送到您的主 moc。另外,不要使用 performBlockAndWait,它当然会阻塞你的 UI。这是一个很好的基础教程,你应该仔细阅读它。 raywenderlich.com/145877/… 除非出于某种原因您想要实现特定目标,否则请避免使用您自己的 CD 队列。
标签: ios core-data magicalrecord