I strive to understand Core Data implementation.
When I examined Core Data implementations I encountered codes as above
In AppDelegate.m
#synthesize managedObjectContext = __managedObjectContext;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSManagedObjectContext *context = [self managedObjectContext];
..
- (NSManagedObjectContext *)managedObjectContext
{
if (__managedObjectContext != nil) {
return __managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
__managedObjectContext = [[NSManagedObjectContext alloc] init];
[__managedObjectContext setPersistentStoreCoordinator:coordinator];
}
return __managedObjectContext;
}
There is a property as
#synthesize managedObjectContext = __managedObjectContext;
and there is a func as
- (NSManagedObjectContext *)managedObjectContext
How it's create a relation between them, how/where it's call managedObjectContext func.
__managedObjectContext is an instance variable.
managedObjectContext is a getter method for __managedObjectContext. It allows you to get __managedObjectContext by, e.g., self.managedObjectContext.
Access of instance variable is usually done through its setter and getter methods.
Related
I am using CoreData in my app and I have two main entities, let's say Cats and Dogs.
I have this method that I use to initialise my context:
- (NSManagedObjectContext *)managedObjectContext
{
NSManagedObjectContext *context = nil;
id delegate = [[UIApplication sharedApplication] delegate];
if ([delegate performSelector:#selector(managedObjectContext)]) {
context = [delegate managedObjectContext];
}
return context;
}
It refers to this method in my App Delegate:
- (NSManagedObjectContext *)managedObjectContext
{
if (_managedObjectContext != nil) {
return _managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
_managedObjectContext = [[NSManagedObjectContext alloc] init];
[_managedObjectContext setPersistentStoreCoordinator:coordinator];
}
return _managedObjectContext;
}
Now, at some point, I have to update some data in my Cats entity.
NSManagedObjectContext *context = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"Cats"];
NSMutableArray *catsArrayData = [[context executeFetchRequest:fetchRequest error:nil] mutableCopy];
NSManagedObject *aCat = [catsArrayData objectAtIndex:i];
NSMutableArray *catsArray = [aCat valueForKey:#"cat"];
[catsArray addObject:responseData];
[aCat setValue:catsArray forKey:#"cat"];
NSError *error = nil;
[context save:&error];
Then, I want to do the same for my Dogs entity. So I use the same code, but for the dogs entity. It's in another part of my code, so I have to redefine:
NSManagedObjectContext *context = [self managedObjectContext];
But the problem is that even though I save this second context, it is actually not saved. The data isn't there. How can I resolve this issue?
You need implement your own change notification mechanism, This is good source to learn it. Multi-Context CoreData.
I have an app where I download the data on startup using a list of operations and it crashes randomly for unknown core data reasons so I spent few days on checking the best practices to update/fetch data in multithreading core data with MagicalRecord. One of the options was to enable the multithreading debugger -com.apple.CoreData.ConcurrencyDebug 1 where Xcode stops the apps when it violates one of their rules. So, Xcode stops my app on this line [SyncRequestEntity MR_createEntityInContext:[self getPrivateContext]]
+ (MagicalRecordVersionNumber) version
{
return MagicalRecordVersionNumber2_3;
}
#implementation NSManagedObjectContext (MagicalRecord)
+ (NSManagedObjectContext *) MR_context
{
return [self MR_contextWithParent:[self MR_rootSavingContext]];
}
+ (NSManagedObjectContext *) MR_contextWithParent:(NSManagedObjectContext *)parentContext
{
NSManagedObjectContext *context = [self MR_newPrivateQueueContext];
[context setParentContext:parentContext];
[context MR_obtainPermanentIDsBeforeSaving];
return context;
}
- (void) MR_obtainPermanentIDsBeforeSaving
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(MR_contextWillSave:)
name:NSManagedObjectContextWillSaveNotification
object:self];
}
+ (NSManagedObjectContext *) MR_newPrivateQueueContext
{
NSManagedObjectContext *context = [[self alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
MRLogInfo(#"Created new private queue context: %#", context);
return context;
}
#end
#implementation MyClass
- (NSManagedObjectContext *) getPrivateContext
{
if (self.privateContext == nil)
{
self.privateContext = [NSManagedObjectContext MR_context];
}
return self.privateContext;
}
- (SyncRequestEntity *) getSyncRequest
{
SyncRequestEntity *syncRequest = [SyncRequestEntity MR_findFirstByAttribute:#"key" withValue:self.itemKey inContext:[self getPrivateContext]];
// Checking if the entity was sync previously with the same filters.
if (syncRequest == nil)
{
syncRequest = [SyncRequestEntity MR_createEntityInContext: [self getPrivateContext]];
}
return syncRequest;
}
#end
#implementation NSManagedObject (MagicalRecord)
+ (id) MR_createEntityInContext:(NSManagedObjectContext *)context
{
if ([self respondsToSelector:#selector(insertInManagedObjectContext:)] && context != nil)
{
id entity = [self performSelector:#selector(insertInManagedObjectContext:) withObject:context];
return entity;
}
else
{
NSEntityDescription *entity = nil;
if (context == nil)
{
entity = [self MR_entityDescription];
}
else
{
entity = [self MR_entityDescriptionInContext:context];
}
if (entity == nil)
{
return nil;
}
return [[self alloc] initWithEntity:entity insertIntoManagedObjectContext:context];
}
}
#end
The privateContext is a local variable for each operation so I have private contexts for each operation in order to not interrupt the main one. The point is that I create one private context for each thread and I'm just trying to create a new NSManagedObject instance using this context and Xcode says that I'm violating the multithreading core data rules. Does anyone have any clue on what's happening?
We have the same issue when developing our own app.
When you try to perform a write operation in a thread different to the context one, it crash sometimes.
Our solution was to make a private manager on the AppDelegate.m file. Just add this code:
- (NSManagedObjectContext *)getPrivateManagedObjectContext
{
if (self.managedObjectContext != nil) {
return self.managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self getPersistentStoreCoordinator];
if (coordinator != nil) {
self.managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[self.managedObjectContext setPersistentStoreCoordinator:coordinator];
}
return self.managedObjectContext;
}
Then when you need to perform any operation, you should use this method that ensures the block is running on the same thread of the context:
[self.managedObjectContext performBlock:^{...}];
[self.managedObjectContext performBlockAndWait:^{...}];
I have looked at other questions and answers but I still cannot set the managedObjectContext of a UITableViewController embedded in a UINavigationController. Here is my current code:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
UIStoryboard *sb = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
UINavigationController *navigationController = [sb instantiateViewControllerWithIdentifier:#"Learn"];
MasterViewController *controller = (MasterViewController *)navigationController.topViewController;
controller.managedObjectContext = self.managedObjectContext;
return YES;
}
However when I run the code, the app crashes and produces a log output of this:
+entityForName: nil is not a legal NSManagedObjectContext parameter searching for entity name
When I try just logging NSLog(#"%#", self.managedObjectContext); in the MasterViewController the result is `(null)
I created my managedObjectContext like so and it received no errors:
- (NSManagedObjectContext *)managedObjectContext {
if (_managedObjectContext != nil) {
return _managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
_managedObjectContext = [[NSManagedObjectContext alloc] init];
[_managedObjectContext setPersistentStoreCoordinator:coordinator];
}
return _managedObjectContext;
}
N.B. I am trying to set a UITableViewController that is not the initial view controller
Any ideas? Thanks in advance!
If you are using the boilerplate Apple template (from Xcode), your app delegate should have a method like this, creating the managed objects dynamically when you call self.managedObjectContext:
- (NSManagedObjectContext *)managedObjectContext
{
if (_managedObjectContext != nil) {
return _managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
_managedObjectContext = [[NSManagedObjectContext alloc] init];
[_managedObjectContext setPersistentStoreCoordinator:coordinator];
}
return _managedObjectContext;
}
Put a breakpoint there and check that you pass the correct context to the view controller. Make sure the VC property is (nonatomic, strong).
If your navigation controller is set as the initial controller in the storyboard, then the problem is that you're instantiating a new instance of that navigation controller with instantiateViewControllerWithIdentifier. You should get the reference to MasterViewController like this instead:
MasterViewController *controller = (MasterViewController *)[(UINavigationController *)self.window.rootViewController topViewController];
When in Xcode 5 I am using the code below, which is in the #interface area, to get data from my CoreData model. It is telling me that I should insert a semi-colon here,
- (NSManagedObjectContext *)managedObjectContext;
Any help is appreciated!
Below is my full code block.
//Get Data
- (NSManagedObjectContext *)managedObjectContext{
NSManagedObjectContext *context = nil;
id delegate = [[UIApplication sharedApplication] delegate];
if ([delegate performSelector:#selector(managedObjectContext)]) {
context = [delegate managedObjectContext];
}
return context;
}
When in Xcode 5 I am using the code below, which is in the #interface area, to get data from my CoreData model.
This is you problem, you're only supposed to but declarations in the #interface. Actual implementations go in the #implementation section.
Your interface goes
#interface
- (NSManagedObjectContext *)managedObjectContext;
#end
In you implementation you put the actual method:
#implementation
...
//Get Data
- (NSManagedObjectContext *)managedObjectContext{
NSManagedObjectContext *context = nil;
id delegate = [[UIApplication sharedApplication] delegate];
if ([delegate respondsToSelector:#selector(managedObjectContext)]) {
context = [delegate managedObjectContext];
}
return context;
}
...
#end
UPDATE:
Thanks to Abizern for noticing that there was another bug in your code: the line [delegate performSelector:#selector(managedObjectContext)] should actually be [delegate respondsToSelector:#selector(managedObjectContext)], because otherwise the application will crash if it doesn't respond to the selector.
you have weird getter in my opinion, try ti use something like this one
- (NSManagedObjectContext *)managedObjectContext {
if (_managedObjectContext != nil)
{
return _managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil)
{
_managedObjectContext = [[NSManagedObjectContext alloc] init];
[_managedObjectContext setPersistentStoreCoordinator:coordinator];
}
return _managedObjectContext;
}
I'm trying to add Core Data to an existing project. I've:
1)added the Core Data framework
2)added the accessors and properties to the AppDelegate
3)created the data model file
Now when I try to call
NSManagedObjectContext *context = [self managedObjectContext];
from a view controller the context is nil and the managedObjectContext never fires.
Here is the AppDelegate:
#import "XXXAppDelegate.h"
#import <CoreData/CoreData.h>
#implementation XXXAppDelegate
#synthesize window=_window;
#synthesize navigationController=_navigationController;
#synthesize managedObjectContext = _managedObjectContext;
#synthesize managedObjectModel = _managedObjectModel;
#synthesize persistentStoreCoordinator = _persistentStoreCoordinator;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
// Add the navigation controller's view to the window and display.
self.window.rootViewController = self.navigationController;
[self.window makeKeyAndVisible];
return YES;
}
// Explicitly write Core Data accessors
- (NSManagedObjectContext *) managedObjectContext {
if (managedObjectContext != nil) {
return managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
managedObjectContext = [[NSManagedObjectContext alloc] init];
[managedObjectContext setPersistentStoreCoordinator: coordinator];
}
return managedObjectContext;
}
- (NSManagedObjectModel *)managedObjectModel {
if (managedObjectModel != nil) {
return managedObjectModel;
}
managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:nil];
return managedObjectModel;
}
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (persistentStoreCoordinator != nil) {
return persistentStoreCoordinator;
}
NSURL *storeUrl = [NSURL fileURLWithPath: [[self applicationDocumentsDirectory]
stringByAppendingPathComponent: #"<Project Name>.sqlite"]];
NSError *error = nil;
persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc]
initWithManagedObjectModel:[self managedObjectModel]];
if(![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil URL:storeUrl options:nil error:&error]) {
/*Error for store creation should be handled in here*/
}
return persistentStoreCoordinator;
}
- (NSString *)applicationDocumentsDirectory {
return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
}
#end
EDIT: here is my view controller code
NSManagedObjectContext *context = [self managedObjectContext];
NSManagedObject *cardSet = [NSEntityDescription insertNewObjectForEntityForName:#"CardSet" inManagedObjectContext:context];
[cardSet setValue:#"Set 1" forKey:#"cardSetName"];
Try adding these condition to check if your managedObjectContext is nil or not wherever you want to use it. If its nil copy it from Appdelegate file.
if (managedObjectContext == nil)
{
managedObjectContext = [(AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
NSLog(#"After managedObjectContext: %#", managedObjectContext);
}
Assuming your properties are declared... you are synthesizing them to ivars with an underscore in front. That's a good thing. However, the only place you want to access them with the underscore is in the implementation of the getter/setter for the property. Unfortunately, that's not happening in any of these. Change it to...
- (NSManagedObjectContext *) managedObjectContext {
if (_managedObjectContext != nil) {
return _managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
_managedObjectContext = [[NSManagedObjectContext alloc] init];
[_managedObjectContext setPersistentStoreCoordinator: coordinator];
}
return _managedObjectContext;
}
- (NSManagedObjectModel *)managedObjectModel {
if (_managedObjectModel != nil) {
return _managedObjectModel;
}
_managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:nil];
return _managedObjectModel;
}
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (_persistentStoreCoordinator != nil) {
return _persistentStoreCoordinator;
}
NSURL *storeUrl = [NSURL fileURLWithPath: [[self applicationDocumentsDirectory]
stringByAppendingPathComponent: #"<Project Name>.sqlite"]];
NSError *error = nil;
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc]
initWithManagedObjectModel:[self managedObjectModel]];
if(![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil URL:storeUrl options:nil error:&error]) {
/*Error for store creation should be handled in here*/
}
return _persistentStoreCoordinator;
}
If you look at the Master-Detail Application template in Xcode you see they pass the ManagedObjectContext in the AppDelegate like this:
#import "AppDelegate.h"
#import "MasterViewController.h"
#implementation AppDelegate
#synthesize managedObjectContext = _managedObjectContext;
#synthesize managedObjectModel = _managedObjectModel;
#synthesize persistentStoreCoordinator = _persistentStoreCoordinator;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
UINavigationController *navigationController = (UINavigationController *)self.window.rootViewController;
MasterViewController *controller = (MasterViewController *)navigationController.topViewController;
NSLog(#"navigationController viewControllers: %#",[navigationController viewControllers]);
NSLog(#"navigationController.topViewController: %#",navigationController.topViewController);
controller.managedObjectContext = self.managedObjectContext;
return YES;
}
If you need a TabBarViewController in front of your app the code looks like this:
#import "AppDelegate.h"
#import "MasterViewController.h"
#implementation AppDelegate
#synthesize managedObjectContext = _managedObjectContext;
#synthesize managedObjectModel = _managedObjectModel;
#synthesize persistentStoreCoordinator = _persistentStoreCoordinator;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
UITabBarController *tabController = (UITabBarController *)self.window.rootViewController;
UINavigationController *navigationController = (UINavigationController *)[[tabController viewControllers] objectAtIndex:0];
MasterViewController *controller = (MasterViewController *)[[navigationController viewControllers] objectAtIndex:0];
controller.managedObjectContext = self.managedObjectContext;
navigationController = (UINavigationController *)[[tabController viewControllers] objectAtIndex:1];
controller = (MasterViewController *)[[navigationController viewControllers] objectAtIndex:0];
controller.managedObjectContext = self.managedObjectContext;
navigationController = (UINavigationController *)[[tabController viewControllers] objectAtIndex:2];
controller = (MasterViewController *)[[navigationController viewControllers] objectAtIndex:0];
controller.managedObjectContext = self.managedObjectContext;
return YES;
}
I don't know how you got past without compiler errors, but your #synthesizes use underscore'd variables that your getters never access. Do it like this:
- (NSManagedObjectContext *) managedObjectContext {
if (_managedObjectContext != nil) {
return _managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
_managedObjectContext = [[NSManagedObjectContext alloc] init];
[_managedObjectContext setPersistentStoreCoordinator: coordinator];
}
return _managedObjectContext;
}
Take note of _managedObjectContext (with underscore). Do the same fixes with managedObjectModel and persistentStoreCoordinator.