问题
In Cocoa, Storyboard's first view controller will call viewDidLoad
(on the first view controller) before AppDelegate
's applicationDidFinishLaunching
is called.
Since I am grabbing my NSManagedObjectContext
in applicationDidFinishLaunching
, I need to wait for applicationDidFinishLaunching
before loading my data.
In other words, in viewDidLoad
, I don't yet have my NSManagedObjectContext
.
What I'm doing now:
I'm adding an applicationDidFinishLaunching
observer in my viewDidLoad
, and load the data when that is triggered.
So (in order):
1. ViewController is adding an applicationDidFinishLaunching
observer.
2. AppDelegates runs its applicationDidFinishLaunching
and triggering the observer.
3. I can load the data from my ViewController.
I realized I'm relaying on viewDidLoad
to be called before applicationDidFinishLaunching
. If that order is changed, the observer will be added after applicationDidFinishLaunching
and data will not load.
Would it be 'safer' to let my 'CoreDataManager' get the NSManagedObjectContext
from AppDelegate directly in its init
?
回答1:
May I suggest a revised design instead, remove any use of core data from the AppDelegate class and move any initialisation into your root view controller instead and then use dependency injection to pass your managed object context to other view controllers (or have a separate core data manager class implemented as a singleton). This will free you from issues like this.
回答2:
The answer provided by Joakim Danielson here is what I ended up doing (credit where credit is due).
--EDIT
Ended up doing exactly what Joakim Danielson suggested. The given code to get the context, may be in the AppDelegate
by default, but it's completely independent. It can be moves anywhere really.
I basically moved the following func into my CoreDataManager
class and I'm calling it in the init()
:
lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "AppName")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error {
// Replace this implementation with code to handle the error appropriately.
fatalError("Unresolved error \(error)")
}
})
return container
}()
Also, not to forget that the saveContext()
in AppDelegate
needs to be updated (by default it points to its own getter).
(Previous answer:)
I've changed (somewhat) the signature lazy var persistentContainer: NSPersistentContainer
provided in the AppDelegate
(by checking CoreData
when creating a project) to: static var persistentContainer: NSPersistentContainer
.
static var coreDataContext: NSManagedObjectContext = {
let container = NSPersistentContainer(name: "AppName")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error {
// Replace this implementation with code to handle the error appropriately.
fatalError("Unresolved error \(error)")
}
})
return container.viewContext
}()
My CoreDataManager
(which is a singleton), can now use this init:
class CoreDataManager {
static let shared = CoreDataManager()
weak var context: NSManagedObjectContext!
init() {
self.context = AppDelegate.coreDataContext
}
Using this pattern (in AppDelegate
):
static var coreDataContext: NSManagedObjectContext = {...}()
Ensures that the block will be executed only once.
Now, it is safe to use the NSManagedObjectContext
at any stage.
来源:https://stackoverflow.com/questions/49864000/initializing-coredata-in-macos