Is there a way to initialize a managed object outside of a context. I'm basically trying to alloc/init a Managed Object outside of a context first, then figure out if I really want to insert the object, and then inject it into the datastore using an existing managed object context.
Is this possible, or does it go against the intended usage of Core Data?
Managed Object are "managed" by the context, therefore you cant really instanciate them with alloc since they are not meant to be.However, instantiating a managed object through the context does not persist it until you call save method on the context, so you would have the same effect using the context to instanciate it and only saving after you figure out that you really want to use the object.
No, you cannot instantiate an NSManagedObject instsance outside of an NSManagedObjectContext (well, you can, but bad things will happen and your program will almost certainly not work as you'd hoped). You can, however, create an NSInMemoryPersistentStore-backed NSManagedObjectContext. It's slightly more setup (not much) and everything vanishes when you dealloc the in-memory store. In the mean time, you get all the benefits of Core Data's object graph management.
For anyone who stumbles upon this question, here's how I accomplished what the OP was probably going for:
NSManagedObjectContext *moc = AppDelegate.managedObjectContext;
NSEntityDescription *entity = [NSEntityDescription entityForName:#"MyModel"
inManagedObjectContext:moc];
MyModel *model = [[MyModel alloc] initWithEntity:entity
insertIntoManagedObjectContext:nil];
What you are probably shooting for is multiple contexts. You can create an additional "scratchpad" context and then merge any changes back into the main context. One of Apple's iPhone sample projects accomplishes this exact thing. You'll have to log into the dev center to find it.
Related
I am new to Core Data and I feel that I don't get a hang of it. When you are going to create a new managed object you have to use the method that I pasted in at the bottom from the NSEntityDescription class.
Now is this object registered in the context, it is right? Why do you have the insertObject: in NSManagedContext then? It works without invoking that method after using the one in NSEntityDescription. Could somebody explain?
NSManagedContext
insertObject:
NSEntityDescription
+ (id)insertNewObjectForEntityForName:(NSString *)entityName inManagedObjectContext:(NSManagedObjectContext *)context
You can create NSManagedObject with nil context and then decide whether you want to add it to context or not.
You may also like this: Is there a way to instantiate a NSManagedObject without inserting it?
I'm just learning core data and mostly coping, but having a little bit of trouble thinking about how I'll implement it.
I want to access the same Core Data from throughout my app. What would be the best way to access it from multiple classes? Would it be recommended to have it as a global variable (normally not recommended in object oriented programming), or to pass it as an instance variable between classes?
Core data model will be available throughout your app. You can easily access the managed object through out your app. You just need to make an instance of the AppDelegate class.
Say for example you have stored contacts. You can just use [managedObject valueForKey:#"name"] in any of the view controllers.
1. Create an instance of the appDelegate
self.theappDel=[[UIApplication sharedApplication] delegate];
2. Get the context,fetch request and entity description.
NSManagedObjectContext*context=[self.theappDel managedObjectContext];
NSEntityDescription*entity=[NSEntityDescription entityForName:#"Contacts" inManagedObjectContext:context];
NSFetchRequest*request=[[NSFetchRequest alloc] init];
[request setEntity:entity];
NSManagedObject*managedObject=nil;
NSError*error=nil;
NSArray*objectList=[context executeFetchRequest:request error:&error];
3. Get the managed object from the array.
if([objectList count]>0)
managedObject=[objectList objectAtIndex:0];
NSLog(#"The name: %#",[managedObject valueForKey:#"name"])
4. Pass the name object using a singleton(or any convenient method) pattern, in other view controllers that you need it.
Pass the NSManagedObjectContext instance, or if you just need to handle one object the NSManagedObject instance, to the next class.
Like it's done in Xcodes Core Data templates.
Take a look at the MagicalRecord Library. Not only does it make a lot of common fetch requests much more succinct, it also makes it easier to access your managed object context just by using [NSManagedObjectContext defaultContext];
I am using a tableview with data from coredata using nsfetchedresultscontroller. When the view loads i make a new entity using
SomeManagedObject *someManagedObject = [NSEntityDescription insertNewObjectForEntityForName:#"SomeManagedObject" inManagedObjectContext:self.managedObjectContext];
This way the new entity appears in my tableview. Now i want this entity to be only temporary, but when i edit some object inside the tableview and save the managedObjectContext the temporary entity will also get saved and i don't want that.
Is their a way to save one object only and not everything inside de managedObjectContext?
Is their some other way to make a temporary object for my tableview.
Any help would be very welcome.
Thanks
Ton
Create the new NSManagedObject with it's alloc init and pass nil instead of the NSManagedObjectContext. Then if you later decide you want that object to be permanent then set it's context. However this will not allow you to see it in a NSFetchedResultsController because it will not be associated with the context.
A better answer can be provided if you could explain what your ultimate goal is.
No, in a managedObjectContext saving is a all or nothing. What I do not know is what happens if you set the persistent store of the managed object to nil
- (void)assignObject:(id)object toPersistentStore:(NSPersistentStore *)store
If you then save the managedObjectContext this object should not be saved. It is just a guess, but tell me if it works ;-)
For temporary managed objects, create them with a 2nd managed object context (MOC). When you are finished, simply release the MOC without performing a save.
Look at the Adding a Book code in CoreDataBooks which uses the same approach to throw away the newly added object when the user cancels.
I've created a program that uses core data and it works beautifully.
I've since attempted to move all my core data methods calls and fetch routines into a class that is self contained. My main program then instantiates that class and makes some basic method calls into that class, and the class then does all the core data stuff behind the scenes. What I'm running into, is that sometimes I'll find that when I grab a managed object from the context, I'll have a valid object, but its properties have been deallocated, and I'll cause a crash. I've played with the zombies and looked for memory leaks, and what I have gathered is it seems that the run loop is probably responsible for deallocating the memory, but I'm not sure.
Is there a way to determine if that memory has been deallocated and force the core data to get it back if I need to access it? My managedObjectContext never gets deallocated, and the fetchedResultsController never does, either.
I thought maybe I needed to use the [managedObjectContext refreshObject:mergeData:] method, or the [managedObjectContext setRetainsRegisteredObjects:] method. Although, I'm under the impression that last one may not be the best bet since it will be more memory intensive (from what I understand).
These errors only popped up when I moved the core data calls into another class file, and they are random when they show up.
Any insight would be appreciated.
-Ryan
Sounds to me like you are not retaining objects you want to keep hanging around. If you are doing something like this:
NSArray *array = [moc executeFetchRequest:request error:&error];
you do not own the returned array and it will most likely disappear when the current autorelease pool is drained. This will occur when the run loop finishes processing the current event.
All this is speculation. If you want a proper answer, you need to post your code.
It's hard to know what the problem is based on your description, but you might want to look at the Core Data memory management guide. You shouldn't have to worry about memory management for managed objects and their entities (they're fetched and faulted automatically). When you talk about "properties," do you mean custom properties backed by ivars? If so, these should be released in didTurnIntoFault and allocd as needed (probably in the accessor).
I was struggling with a similar issue. I'm using a managed object class and want to set its properties dependent on user input. But the sometimes the properties and sometimes the whole managed object were deallocated.
After reading the Apple documentation http://developer.apple.com/library/IOs/#documentation/Cocoa/Conceptual/CoreData/Articles/cdMemory.html the chapter "The Role of the Managed Object Context" I learned that managed objects are released each run loop completes.
And there is the golden advice to set
[myMangedObjectContext setRetainsRegisteredObjects:YES];
(I had to set it in the init method (initWithNibName for me) of my view controller.)
You should also regard to retain only the objects you need to as explained in the documentation. But read it yourself.
If I'm not right please correct me.
I also made a class that handles all my CoreData fetching and stuff. I ran into a couple of gotcha's, so here are some tips. (If I am making any memory management errors in these examples, please let me know.)
Two things:
1) Made a "fetchFiredObject" method in the CoreData handler class. So when I want to get a managedObject that has all its variables and is a "fully feathered bird" so to speak, instead of doing:
aManagedObject *myManagedObject = [myCoreDataHandler.managedObjectStorageArray objectAtIndex:1];
int x = myManagedObject.someVariable.intValue;
instead I do:
aManagedObject *myManagedObject = [myCoreDataHandler fetchFiredObjectAtIndex:1];
int x = myManagedObject.someVariable.intValue;
And in myCoreDataHandler's fetchFiredObjectAtIndex:i method, we're going into the array, finding the object key at index i, then doing a fetchRequest for that object key, and returning the freshly-fetched managedObject so that it won't have been faulted or deallocated, etc. :D
2) When I create a new child viewController, I populate its "myCoreDataHandler" value from the parent upon creation. However, this happens on a subsequent line of code after the line of code that creates the new viewController. Therefore, any code in the child's viewDidLoad that tries to use myCoreDataHandler's methods will return empty objects because viewDidLoad completes before the parent's next line of code where it sets the values of globals in the child object. So make sure you are not accessing your "Core Data handling object" from within viewDidLoad or anything local methods called by viewDidLoad! Instead call them from the parent after creating the new viewController.
I'm slightly confused in one aspect of Core Data. That is, when do I use the rudimentary alloc/init routine vs created an object with core data and saving it into the current managed object context.
I know that's a rather vague question, so let me give you an example.
I have an application I'm currently working on that iterates through all of a user's contact book on the iPhone. From there, I wrote a model class called 'Person'. I used to do something like this in a loop of people:
Person *person = [[Person alloc] initWithWrapper:mywrapper];
mywrapper would contain an NSDictionary with the attributes for person. Later I'd be able to populate the address book in my app with the person objects.
Now I've started rebuilding parts of the app with Core Data. Do I continue using the strategy above to populate my address book? Or do I do something like this instead:
Person *person = (Person *)[NSEntityDescription insertNewObjectForEntityForName:#"Person" inManagedObjectContext:managedObjectContext];
[person setName:name];
[person setDob:dob];
// Commit the change.
NSError *error;
if (![managedObjectContext save:&error]) {
// Handle the error.
}
The problem is, this code gets executed everytime the app gets started. Should I not be using core data as it will populate the storage mechanism with redundant instances of person everytime the app loads? Should I modify my NSManagedObject (Person class) and add my initWithWrapper: method and continue as I normally would there?
Slightly confused, would love clarification.
You should never be initializing Core Data objects outside of a managed object context - there's simply no point. Having some
Person *person = [[[Person alloc] init] autorelease];
does you no good since you can't save the object, manipulate it, or really do anything useful that Core Data provides without the context (and thus model and store coordinator) backing it up.
You should instead only use the alloc-init combo when you are inserting an object into Core Data for the first time; this is what the initWithEntity:insertIntoManagedObjectContext: method is for. And you're right, every time you call that method you are inserting a new object into the Core Data context and therefore store, and you may wind up with duplicate objects if you're not careful.
What I would instead recommend for you, if you're running code on every startup, is to come up with a Core Data query that returns some set of existing Person objects, and only add objects (using the initialization method) that don't already exist in the store. If the object already exists, modify it instead of creating a new one.
The trick is getting something like this to perform properly. You shouldn't do a Core Data fetch for every contact in the iPhone address book; many small fetches like this are very expensive. You could in theory get two NSSets - one of Person objects, and one of contacts - then compare them by some unique key (like a hash of the first and last names of the contact). I leave the optimization to you.
The key point is this: don't use alloc and init on a Core Data object unless you mean to insert that object for the first time into a context. Instead look at your existing objects and modify them if necessary.
Yeah, it's simplest to add the initWithWrapper method to your Person class. It would be something like this:
- (id) initWithWrapper:(NSDictionary *)wrapper {
NSEntityDescription * person = [NSEntityDescription entityForName:#"Person" inManagedObjectContext:someMOC];
if (self = [super initWithEntity:person insertIntoManagedObjectContext:someMOC]) {
//do your wrapperly initialization here
}
return self;
}
The only downside to this is that this method has to know which managedObjectContext it should insert the object into, so you have to figure out a way to provide that.
That being said, I use this pattern myself all the time.