What is the difference between the global nsmanagedobjectcontext retrieved from appdelegate and the nsmanagedobjectcontext retrieved from nsmanagedobject
1)
AppDelegate *delegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = delegate.managedObjectContext;
2)
NSManagedObject *employee1 = [[NSManagedObject alloc]initWithEntity:entity insertIntoManagedObjectContext:context];
NSManagedObjectContext *context = employee1.managedObjectContext;
If you get an instance of NSManagedObjectContext from your app delegate, it's whatever instance you create in your app delegate, configured in whatever way you have configured it.
If you ask a managed object for the value of its managedObjectContext property, you get whatever context was used to create it or fetch it.
These might be the same or they might not be, depending on how many instances of NSManagedObject you have created and how you are using them.
Related
I have a class with a method like this:
- (void)getEntities
{
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *mainContext = appDelegate.managedObjectContext;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"MyEntity"];
NSArray *entities = [mainContext executeFetchRequest:fetchRequest error:nil];
for (NSManagedObject *item in entities) {
NSLog(#"Name: %#", ((MyEntity *)item).name);
}
}
I have enabled the com.apple.CoreData.ConcurrencyDebug 1 and when I call this method (in main thread) I get the error:
CoreData: error: The current thread is not the recognized owner of this NSManagedObjectContext(0x16d40160). Illegal access during executeFetchRequest:error:
However, if I do:
- (void)getEntities
{
NSManagedObjectContext *privateContext = [CoreDataStack getPrivateContext];
if (privateContext != nil) {
[privateContext performBlock: ^{
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"MyEntity"];
NSArray *entities = [privateContext executeFetchRequest:fetchRequest error:nil];
for (NSManagedObject *item in entities) {
NSLog(#"Name: %#", ((MyEntity *)item).name);
}
}];
}
}
And I call it also in main thread, it does not seem to cause such Core Data error... why? I've been always assuming that the NSManagedObjectContext provided by default in AppDelegate and I'm retrieving belongs to main thread... What I'm doing wrong or missing?
The entities I'm trying to fetch using the context from AppDelegate where previously saved from a context that was created in a private queue (similar to the one I create in my second code snippet). Could that be the cause of the error? Such private context was not a child of any other context, and I had no error when saving it.
I need to get the entities I have already stored by using a private context in the main thread, to keep them in an NSArray in the implementation of an UIViewController.
Thanks so much in advance.
It seems that the problem wasn't in the method but my initialization of the context in AppDelegate: before calling the method in the question, and in certain scenario I was retrieving the context the first time from a queue that wasn't the main thread, and that seemed to be causing the context to be initialized in that other queue.
Inside my iOS application, I am using Core Data to do a fetch, and delete of a very large data set. This process takes approximately 5-10 seconds. What I would like to do is perform a rollback in case the user decides to turn the device off before the process has completed. However, the problem is to have the SAME instance of the NSManagedObjectContext to call the rollback function from the appropriate AppDelegate method. Within my application, I call my Core Data methods using a Singleton object like this:
static MySingleton *sharedSingleton = nil;
+ (MySingleton *) sharedInstance {
if (sharedSingleton == nil) {
sharedSingleton = [[super alloc] init];
}
return sharedSingleton;
}
In my application, I return an instance of an NSManagedObjectContext like this:
- (NSManagedObjectContext *) managedObjectContext{
if (_managedObjectContext != nil) {
return _managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
_managedObjectContext = [[NSManagedObjectContext alloc]initWithConcurrencyType:NSMainQueueConcurrencyType];
[_managedObjectContext setPersistentStoreCoordinator:coordinator];
//Undo Support
NSUndoManager *anUndoManager = [[NSUndoManager alloc] init];
[self.managedObjectContext setUndoManager:anUndoManager];
}
return _managedObjectContext;
}
I then call it, and assign it to a reference like this:
NSManagedObjectContext *context = [[MySingleton sharedInstance] managedObjectContext];
How would I make this instance of the ManagedObjectContext available to me for use in the AppDelegate, so that I can call the rollback function?
First off, the better (safer) way to create a singleton is as in the example given here: Create singleton using GCD's dispatch_once in Objective C, namely:
+ (instancetype)sharedInstance
{
static dispatch_once_t once;
static id sharedInstance;
dispatch_once(&once, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
Next, since you've created a managed object context that must hang around for a few seconds, you must have a strong reference to it somewhere, which you do.
If you're in the midst of debugging and are questioning some fundamental assumptions about your code, give the managed object context a name (or log the memory address of the MOC pointer), that you can inspect in the debugger later, to verify for yourself that indeed, you're dealing with the same one.
Note also that if you created a dedicated Managed Object Context just for this sort of importing, you wouldn't need to roll it back. You could just discard it.
In my apps, I usually have a parent (root) managed object context and a couple of child contexts; one child is for the main thread, another child is for import type operations.
As an alternative solution, instead of rollback the changes, you can create a multi context scenario where a child managed object context add all the data that you need and eventually when its done, you save the child context sending the new data to the main managed object. This way the main managed object context its not affected until the complete process is done.
This is a great article as reference Multi-Context CoreData.
Basically what you need to do is
// create main MOC
_mainContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[_mainContext setPersistentStoreCoordinator:_persistentStoreCoordinator];
// create child MOC
_childContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
_childContext.parentContext = _mainContext;
Hope it helps.
#implementation FirstScene
...
- (void)nextScene {
Meetings *meetings = (Meetings *) [NSEntityDescription insertNewObjectForEntityForName:#"Meetings" inManagedObjectContext:self.managedObjectContext];
NSManagedObjectID* objectID = [meetings objectID];
[secondScene setObjectID:objectID];
}
...
#end
#implementation SecondScene
....
- (void)viewDidLoad
{
[super viewDidLoad];
self.managedObjectContext = [(STAppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
}
+ (void)setObjectID:(NSManagedObjectID*)objectID {
NSManagedObjectContext *context = [self managedObjectContext]; // ERROR
Meetings *theSameMeetings = (Meetings *)[context objectWithID:objectID];
}
...
#end
I'm getting an error "No known class method for selector 'managedObjectContext' in the following code:
NSManagedObjectContext *context = [self managedObjectContext];
My objective here is to pass the objectID of *meetings (managedObjectContext) in FirstScene to the SecondScene so that I can continue to add to the entity *meetings attributes. Thanks.
That's not a Core Data error, it's a basic Objective-C error. You're in this method:
+ (void)setObjectID:(NSManagedObjectID*)objectID;
The "+" says that this is a class method, not an instance method. So self in this case is the SecondScene class, not any particular instance of that class. When you try to do this:
NSManagedObjectContext *context = [self managedObjectContext];
...you're trying to call a method named +managedObjectContext, i.e. a class method with that name. That apparently doesn't exist, which isn't too surprising.
I suspect you intended for setObjectID: to be an instance method, which means it should have a - instead of a + at the start of the line.
Brother, as it says you do not have 'managedObjectContext' method in your 'SecondScene'. is it?
Apparently you are a beginner to core data and you have just stepped into it.
however, Go to AppDelegate of your project, and search for '-(void)managedObjectContext' and you will see that there is a manageobjectcontext method there, replace [self manageobjectcontext] with the method from AppDelegate.
This will allow you to have access to appdelegate
YourAppDelegateClass *appDelegate = (YourAppDelegate *)[[UIApplication sharedApplication] delegate]; and now call [appDelegate managedObjectContext];
Hope this helps.
By calling [self managedObjectContext] the compiler is looking for a method name in the current target that is -(void)managedObjectContext which clearly doesn't exist. The best way to pass an object ID is set a property and synthesize that property, that will allow you to assign the objectID to an instance of meeting, and then retrieve it from said instance.
--EDIT--
In whichever class you need to access your ManagedObjectID you want to declare your delegate as such:
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
Then in your setObjectID method you can call:
NSManagedObjectContext *context = [appDelegate managedObjectContext];
I have a core data setup that has 2 database entities. For the sake of names I'll call them Primary and Secondary. Secondary only belong to one Primary (relationship is setup). In my main view that lists the Primary objects in a table I retrieved them and put them in an PriObject Class which stores it's properties (including the managed object ID). The PriObject is then added to a mutable array (priArray), which is then used to fill the table with the data. All works ok so far. When I then click on the row I can log the PriObject.moID.
I can't figure out how to lookup that object in the database so I can then add Secondary objects to it. I can't do it by name because some Primary might have the same name.
I need to work out how to get an object back from either the URI or the ID. I have the ID so I can generate the URI if I need to.
Can't get my head around it at all and any examples I have found while looking don't cover what I need. What options are there?
EDIT: I am currently getting all my objects with the following.
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSEntityDescription *entityDesc = [NSEntityDescription entityForName:#"Primary"
inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entityDesc];
NSError *error;
NSArray *objects = [context executeFetchRequest:request
error:&error];
if ([objects count] == 0) {
NSLog(#"Nothing found");
} else {
NSLog(#"Something found");
}
How can I change this for just the one using:
ObjectWithID:
Call the objectWithID: method on your NSManagedObjectContext instance to retrieve the instance.
As an aside, it seems like you are making things harder on yourself with this PriObject class, it seems to be a wrapper around your NSManagedObject instances, is that right? I'd just use NSManagedObject subclasses directly, personally.
i have a core data database that start from the AppDelegate, and there i'll do this:
MasterViewController *masterViewController = [[[MasterViewController alloc] initWithNibName:#"MasterViewController" bundle:nil] autorelease];
masterViewController.managedObjectContext = self.managedObjectContext;
so in this way i can access to the database in the master view, then i want access to the core data in other classes, and i do this:
id delegateContext = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [delegateContext managedObjectContext];
but when i add, and access the information, i have some bad Exc access and other error in various part of the code when i try to access to the information of the database, so i think that maybe i have used in bad way the core data, to access the information from other classes.
maybe i have to release the delegatecontext?...i haven't release it in any class i have used it, and if i have release it, where i have do it?
anyone can help me?
Problem is you are taking delegateContext of type id instead of NSManagedObjectContext and assigning it to *contextin these two lines.
id delegateContext = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [delegateContext managedObjectContext];
How about adding these lines whenever you need to save or access managedObjectContext, check it if its nil if YES copy it from Appdelegate.
if (managedObjectContext == nil)
{
managedObjectContext = [(AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
NSLog(#"After managedObjectContext: %#", managedObjectContext);
}
Just try to remove your these two lines and add the above if condition I am suggesting. Should work.
Update 1: As you are asking I am mentioning steps for creating
Appdelegate's instance to access global properties.
#import "AppDelegate.h"
after #implementation create instance of Appdelegate
AppDelegate *app;
in viewDidLoad method...
app=(AppDelegate *)[[UIApplication sharedApplication]delegate];