使用 CoreData 和 NSFetchedResultsController 使用可选的 managedObject 查找 nil
Finding nil with optional managedObject, using CoreData and NSFetchedResultsController
我正在尝试用一些 CoreData 填充 collectionView。我在 Swift 中编程并使用 iOS 9。我已经正确连接了大部分内容(据我所知),但由于一行代码,我一直崩溃。
这是我 viewController 的代码。错误为 nil 的行是 "managedObjectContext: coreDataStack.context":
override func viewDidLoad() {
super.viewDidLoad()
//1
let fetchRequest = NSFetchRequest(entityName: "Family")
let firstNameSort =
NSSortDescriptor(key: "firstName", ascending: true)
fetchRequest.sortDescriptors = [firstNameSort]
//2
fetchedResultsController =
NSFetchedResultsController(fetchRequest: fetchRequest,
managedObjectContext: coreDataStack.context,
sectionNameKeyPath: nil,
cacheName: nil)
fetchedResultsController.delegate = CollectionViewFetchedResultsControllerDelegate(collectionView: familyCollectionView)
//3
do {
try fetchedResultsController.performFetch()
} catch let error as NSError {
print("Error: \(error.localizedDescription)")
}
}
下面是我引用的 "CoreDataStack" 对象:
import Foundation
import CoreData
class CoreDataStack {
let modelName = "CoreDataModel"
lazy var context: NSManagedObjectContext = {
var managedObjectContext = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType)
managedObjectContext.persistentStoreCoordinator = self.psc
return managedObjectContext
}()
private lazy var psc: NSPersistentStoreCoordinator = {
let coordinator = NSPersistentStoreCoordinator(
managedObjectModel: self.managedObjectModel)
let url = self.applicationDocumentsDirectory
.URLByAppendingPathComponent(self.modelName)
do {
let options =
[NSMigratePersistentStoresAutomaticallyOption : true]
try coordinator.addPersistentStoreWithType(
NSSQLiteStoreType, configuration: nil, URL: url,
options: options)
} catch {
print("Error adding persistent store.")
}
return coordinator
}()
private lazy var managedObjectModel: NSManagedObjectModel = {
let modelURL = NSBundle.mainBundle()
.URLForResource(self.modelName,
withExtension: "momd")!
return NSManagedObjectModel(contentsOfURL: modelURL)!
}()
private lazy var applicationDocumentsDirectory: NSURL = {
let urls = NSFileManager.defaultManager().URLsForDirectory(
.DocumentDirectory, inDomains: .UserDomainMask)
return urls[urls.count-1]
}()
func saveContext () {
if context.hasChanges {
do {
try context.save()
} catch let error as NSError {
print("Error: \(error.localizedDescription)")
abort()
}
}
}
}
关于为什么我在拨打那条线路时接听 nil 的任何想法?
存在问题是因为 self.coreDataStack 从未分配过,但是在定义 fetchedResultsController 时访问了它。
为了简单地解决问题,在访问之前创建 CoreDataStuck 实例并将其分配给视图控制器的 coreDataStack 属性:
self.coreDataStack = CoreDataStack()
不过,我想强调管理 NSManagedObjectContext 的重要性。我相信创建 CoreDataStack 实例并在需要时传递对它的引用是一种更常见的模式。例如,您可以在您的 Application Delegate 中创建它的一个实例,并将引用分配给您的第一个视图控制器,如下所示:
lazy var cds = CoreDataStack()
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSOBject: AnyObject?]?) -> Bool {
// Obtain a reference to your root view controller, this may have to be different if you use different types of controllers. This would be the View controller where you use the core data stack in your question though.
let vc = window!.rootViewController as! ViewController
vc.coreDataStack = self.cds // This will now ensure the property on your view controller is assigned and can be accessed.
return true
}
稍后在您的应用程序中,我会建议传递相同的引用。您可以通过访问应用程序委托来完成此操作,我个人认为通过 prepare for segue 方法传递引用更简洁。我只是想像你的应用程序中存在一个 SecondViewController class 并且你正在从问题中的 ViewController 过渡到它:
class SecondViewController: UIViewController {
var cds: CoreDataStack!
}
我假设在 ViewController 和 SecondViewController 之间有一个名为 "next" 的 segue。在ViewController文件中,定义prepareForSegue方法:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "next" {
let secondVc = segue.destinationViewController as! SecondViewController
secondVc.cds = self.coreDataStack
}
}
我试图强调保留对首次创建的核心数据堆栈实例的引用的想法,我认为随着您的继续,您会发现它更有用。
我正在尝试用一些 CoreData 填充 collectionView。我在 Swift 中编程并使用 iOS 9。我已经正确连接了大部分内容(据我所知),但由于一行代码,我一直崩溃。
这是我 viewController 的代码。错误为 nil 的行是 "managedObjectContext: coreDataStack.context":
override func viewDidLoad() {
super.viewDidLoad()
//1
let fetchRequest = NSFetchRequest(entityName: "Family")
let firstNameSort =
NSSortDescriptor(key: "firstName", ascending: true)
fetchRequest.sortDescriptors = [firstNameSort]
//2
fetchedResultsController =
NSFetchedResultsController(fetchRequest: fetchRequest,
managedObjectContext: coreDataStack.context,
sectionNameKeyPath: nil,
cacheName: nil)
fetchedResultsController.delegate = CollectionViewFetchedResultsControllerDelegate(collectionView: familyCollectionView)
//3
do {
try fetchedResultsController.performFetch()
} catch let error as NSError {
print("Error: \(error.localizedDescription)")
}
}
下面是我引用的 "CoreDataStack" 对象:
import Foundation
import CoreData
class CoreDataStack {
let modelName = "CoreDataModel"
lazy var context: NSManagedObjectContext = {
var managedObjectContext = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType)
managedObjectContext.persistentStoreCoordinator = self.psc
return managedObjectContext
}()
private lazy var psc: NSPersistentStoreCoordinator = {
let coordinator = NSPersistentStoreCoordinator(
managedObjectModel: self.managedObjectModel)
let url = self.applicationDocumentsDirectory
.URLByAppendingPathComponent(self.modelName)
do {
let options =
[NSMigratePersistentStoresAutomaticallyOption : true]
try coordinator.addPersistentStoreWithType(
NSSQLiteStoreType, configuration: nil, URL: url,
options: options)
} catch {
print("Error adding persistent store.")
}
return coordinator
}()
private lazy var managedObjectModel: NSManagedObjectModel = {
let modelURL = NSBundle.mainBundle()
.URLForResource(self.modelName,
withExtension: "momd")!
return NSManagedObjectModel(contentsOfURL: modelURL)!
}()
private lazy var applicationDocumentsDirectory: NSURL = {
let urls = NSFileManager.defaultManager().URLsForDirectory(
.DocumentDirectory, inDomains: .UserDomainMask)
return urls[urls.count-1]
}()
func saveContext () {
if context.hasChanges {
do {
try context.save()
} catch let error as NSError {
print("Error: \(error.localizedDescription)")
abort()
}
}
}
}
关于为什么我在拨打那条线路时接听 nil 的任何想法?
存在问题是因为 self.coreDataStack 从未分配过,但是在定义 fetchedResultsController 时访问了它。
为了简单地解决问题,在访问之前创建 CoreDataStuck 实例并将其分配给视图控制器的 coreDataStack 属性:
self.coreDataStack = CoreDataStack()
不过,我想强调管理 NSManagedObjectContext 的重要性。我相信创建 CoreDataStack 实例并在需要时传递对它的引用是一种更常见的模式。例如,您可以在您的 Application Delegate 中创建它的一个实例,并将引用分配给您的第一个视图控制器,如下所示:
lazy var cds = CoreDataStack()
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSOBject: AnyObject?]?) -> Bool {
// Obtain a reference to your root view controller, this may have to be different if you use different types of controllers. This would be the View controller where you use the core data stack in your question though.
let vc = window!.rootViewController as! ViewController
vc.coreDataStack = self.cds // This will now ensure the property on your view controller is assigned and can be accessed.
return true
}
稍后在您的应用程序中,我会建议传递相同的引用。您可以通过访问应用程序委托来完成此操作,我个人认为通过 prepare for segue 方法传递引用更简洁。我只是想像你的应用程序中存在一个 SecondViewController class 并且你正在从问题中的 ViewController 过渡到它:
class SecondViewController: UIViewController {
var cds: CoreDataStack!
}
我假设在 ViewController 和 SecondViewController 之间有一个名为 "next" 的 segue。在ViewController文件中,定义prepareForSegue方法:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "next" {
let secondVc = segue.destinationViewController as! SecondViewController
secondVc.cds = self.coreDataStack
}
}
我试图强调保留对首次创建的核心数据堆栈实例的引用的想法,我认为随着您的继续,您会发现它更有用。