Я использую MagicalRecord для своего проекта, и в моей базе данных есть объект CDSong, за который могут проголосовать несколько объектов CDVoter.
Избиратели добавляются и удаляются в фоновом режиме с помощью NSManagedObjectContext.performAndWait(block:)
, вызываемого из очереди последовательной отправки. У меня есть NSFetchedResultsController
, который извлекает CDSongs и отображает их избирателей (в этом простом сценарии он печатает только имена избирателей).
Все было бы хорошо, но я иногда получаю сбои в методе NSFetchedResultsControllerDelegate controllerDidChangeContent: - / Согласно моему анализу, в отношениях CDSong.voters появляются недопустимые пустые объекты CDVoter (name = nil, votedSong = nil). Эти пустые избиратели не возвращаются из CDVoter.mr_findAll()
.
Это код, имитирующий сбой (обычно после <20 нажатий кнопки приложение вылетает, потому что имя CDVoter равно нулю). Я что-то не так делаю с контекстами и сохранением? Поместите сюда весь тестовый код с инициализацией базы данных и frc, если кто-то хочет попробовать, но проблемная часть находится в методах 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 :).