When loading an existing document using NSPersistentDocument
, as part of initialization I'd like to prepare some content:
NSFetchRequest *req = [NSFetchRequest fetchRequestWithEntityName:@"DocumentRoot"];
NSArray *results = [self.managedObjectContext executeFetchRequest:req error:NULL];
if (results.count) self._docRoot = [results objectAtIndex:0];
When I put this code in -init
, the fetch request doesn't return any results.
I encountered this problem while refactoring the view-controller components from my NSPersistentDocument
subclass to a new NSWindowController
subclass. I used to handle this initialization in -windowControllerDidLoadNib:
, but that isn't called anymore.
If I move the code from -init
to -makeWindowControllers
I get the results I expect. Is -makeWindowControllers
really the right place to prepare content like this?
Based on the responses I've gotten I think I'm doing the right thing, so here's my answer to my own question.
If you're using the Core Data stack provided by NSPersistentDocument, you can not use Core Data in -init
.
Instead, you should:
- Put the document-initialization code directly in
-windowControllerDidLoadNib:
– or if you use a custom NSWindowController subclass, in-makeWindowControllers
. - You may also abstract the document-initialization code into a helper method with some unique name like
-setUpDocument
, and call that method from-makeWindowControllers
/-windowControllerDidLoadNib:
instead.
If you're using a plain NSDocument, or you're setting up the Core Data stack on your own, you can set up the document model in -init
.
From this question and your related question about NSArrayControllers, I'm gathering that you're doing something like this:
- (void)makeWindowControllers
{
MyWindowController* wc = [[[MyWindowController alloc] initWithWindowNibName: [self windowNibName]] autorelease];
[self addWindowController: wc];
}
When you do this, -windowControllerDidLoadNib:
won't be called, because the NSDocument object isn't the Nib's owner if you init that way. If you look at NSDocument.h
you'll see the following comment (see added emphasis):
/* Create the user interface for this document, but don't show it yet. The
default implementation of this method invokes [self windowNibName],
creates a new window controller using the resulting nib name (if it is
not nil), **specifying this document as the nib file's owner**, and then
invokes [self addWindowController:theNewWindowController] to attach it.
You can override this method to use a custom subclass of
NSWindowController or to create more than one window controller right
away. NSDocumentController invokes this method when creating or opening
new documents.
*/
- (void)makeWindowControllers;
If you, instead, do this:
- (void)makeWindowControllers
{
MyWindowController* wc = [[[MyWindowController alloc] initWithWindowNibName: [self windowNibName] owner: self] autorelease];
[self addWindowController: wc];
}
I believe you'll find that -windowControllerDidLoadNib:
is called again. That may not help you, if you have a good reason for that Nib's owner to not be the NSDocument, but that's why -windowControllerDidLoadNib:
isn't being called, and what you can do to get that behavior back. That's almost certainly a better place to be doing fetches than in init, which likely happens before all the necessary CoreData support stuff is in place. So that's one option.
If the code is not called from init that is because your document is being initialized elsewhere such as initWithContentsOfURL:ofType:error:
, initForURL:withContentsOfURL:ofType:error:
, initWithType:error:
or initWithCoder:
makeWindowControllers
is not for setting up your data. Try implementing all of the above initializers and log to see which is getting called.
来源:https://stackoverflow.com/questions/7734701/is-makewindowcontrollers-the-best-place-to-initialize-an-nspersistentdocument