NSFetchedResultsController not receiving new objects - ios

I'm trying to update a UITableView in a view called "HomeViewController" to show the new objects that I've added. But the NSFetchedResultsController doesn't update after I've finished creating a new one. I have the following code in various documents:
HomeViewController.h
- (void)saveNewShindyToDatabaseForName:(NSString *)name
details:(NSString *)details
dateAndTime:(NSDate *)dateAndTime
location:(CLLocation *)location
timePosted:(NSDate *)postedTime
{
if (!self.shindysDatabase) {
NSURL *url = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
self.shindysDatabase = [[UIManagedDocument alloc] initWithFileURL:url];
}
dispatch_queue_t saveQ = dispatch_queue_create("Shindy Creator", nil);
dispatch_async(saveQ, ^{
[self.shindysDatabase.managedObjectContext performBlock:^{
[Shindy setupShindyForSaveToDatabaseForName:name
details:details
dateAndTime:dateAndTime
location:location
timePosted:postedTime
inManagedObjectcontext:self.shindysDatabase.managedObjectContext];
}];
});
NSError *error = nil;
if (![self.shindysDatabase.managedObjectContext save:&error]) {
NSLog(#"%#", error.userInfo);
}
}
- (void)setupFetchedResultsController
{
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Shindy"];
NSSortDescriptor *nameSortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"name" ascending:YES];
NSSortDescriptor *photoSortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"photo" ascending:YES];
NSSortDescriptor *detailSortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"details" ascending:YES];
NSSortDescriptor *dateAndTimeSortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"dateAndTime" ascending:YES];
NSSortDescriptor *timePostedSortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"timePosted" ascending:YES];
request.sortDescriptors = [NSArray arrayWithObjects:nameSortDescriptor, photoSortDescriptor, detailSortDescriptor, dateAndTimeSortDescriptor, timePostedSortDescriptor, nil];
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request
managedObjectContext:self.shindysDatabase.managedObjectContext
sectionNameKeyPath:nil
cacheName:nil];
}
Shindy+SQL.m (An NSManagedObject subclass of the entity, "Shindy")
+ (Shindy *)setupShindyForSaveToDatabaseForName:(NSString *)name
details:(NSString *)details
dateAndTime:(NSDate *)dateAndTime
location:(CLLocation *)location
timePosted:(NSDate *)postedTime
inManagedObjectcontext:(NSManagedObjectContext *)context
{
Shindy *shindy = [NSEntityDescription insertNewObjectForEntityForName:#"Shindy" inManagedObjectContext:context];
__block NSString *nameString = name;
__block FBProfilePictureView *profileImage = nil;
if (FBSession.activeSession.isOpen) {
[[FBRequest requestForMe] startWithCompletionHandler:
^(FBRequestConnection *connection, NSDictionary<FBGraphUser> *user, NSError *error) {
if (!error) {
nameString = user.name;
profileImage.profileID = user.id;
}
}];
}
NSData *imageData = nil;
for (NSObject *obj in [profileImage subviews]) {
if ([obj isMemberOfClass:[UIImageView class]]) {
UIImageView *objImg = (UIImageView *)obj;
imageData = UIImagePNGRepresentation(objImg.image);;
break;
}
}
shindy.name = name;
shindy.dateAndTime = dateAndTime;
shindy.timePosted = postedTime;
shindy.details = details;
shindy.photo = imageData;
return shindy;
}
The method, - (void)setupFetchedResultsController: is called when the view is initially loaded (but not in the viewDidLoad or viewWillAppear method). And my - (void)saveNewShindyToDatabaseForName: function is called in a separate view controller once the user is finished entering their data. In other words, it's public API, but the NSManagedObjectContext remains within the HomeViewController for delegation. Also, the delegates & data sources for my code are set properly.
I have tried to force every kind of fetch; whether it be through the managedObjectContext, or the fetchedResultsController. I have also set an NSLog in various places of my code to ensure that the data is getting through to the HomeViewController. I have also set another NSLog to ensure that both the fetchedResultsController & managedObjectContext is not nil.
Everything seems to check out, and I don't get any sort of errors from the other default errors currently within my code.
I fear that this has to do something with the NSSortDescriptors I have in my - (void)setupFetchedResultsController: method, but I have also tried to configure those in various ways to no avail. There is a definite connection to my managedObjectContext & my fetchedResultsController as well, so that's not the problem.
I've simply run out of things to try. Perhaps, someone on here has some insight.
For jrturton...
I have implemented the following code in my - (void)saveNewShindyToDatabaseForName: method after all of the code that was there originally. It looks like this:
- (void)saveNewShindyToDatabaseForName:(NSString *)name
details:(NSString *)details
dateAndTime:(NSDate *)dateAndTime
location:(CLLocation *)location
timePosted:(NSDate *)postedTime
{
// All of the code originally in this method is here...
if ([self.fetchedResultsController managedObjectContext] == self.shindysDatabase.managedObjectContext) {
NSLog(#"MOC's are the same.");
}
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Shindy"];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"details" ascending:YES];
request.sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSError *error = nil;
NSArray *detailsArray = [self.shindysDatabase.managedObjectContext executeFetchRequest:request error:&error];
if (error) {
NSLog(#"error: %#", error.userInfo);
}
NSLog(#"details: %#", detailsArray);
}
What all of this returns is that to answer your second possible answer, the MOC's are definitely the same. However, it appears as though the new objects are not actually getting saved into Core Data. Or I'm not pulling them back out properly.
The only way I could think of logging what is inside of my Core Data without getting an unrecognized selector error was by creating a new request for the correct entity, and then executing a fetch, then logging the results of that fetch, which, in this case, returned nothing.
I'm going to assume then that my problem lies within my code not saving the new objects to Core Data. With that said, can you identify anywhere I'm not properly saving these objects to my context so that it can be put into my Core Data model for retrieval later? Or do I need to setup a different method other than requesting to log what's being saved into my Core Data?

Possible issues:
No fetched results controller delegate (ok, resolved according to comments).
The FRC is potentially being set up before you have a database (you create a new database inside the first method, but say the FRC is set up before that - do they have the same MOC object?
Multithreading hell - your MOC save call will be executed before the block that calls the method that calls the method that passes the completion block (phew!). This could well result in the notifications not catching up.
You don't mention if the new objects ever enter the database - if you save, close and re-open the app, are they there? Are we dealing with a failure to send or receive change notifications, or a failure to make the changes?

Related

Core Data Lazy Loading with NSPrivateQueueConcurrencyType and custom setter not working

Problem: Fetching a managed object using a background thread does not lazy load the NSManaged object relationship correctly when the NSManaged object that is related has a custom setter. Doing fetch on main thread with main concurrency type works without a problem. Why is this?
Work Around: If I create a custom getter on the relationship object and check for nil, I can force the NSManaged object to load by calling other variables that don't have custom setter methods.
Background
The core data layout is pretty simple. I have a Game managed object and a Turn managed object. The turn object is a one to one relationship with the game object. I always fetch the game object in order to access the turn object. TurnImp and GameImp are implementation classes that inherit from the Game and Turn object so I don't put getter/setter methods in auto generated code.
Code
The Fetch
//
//Stick command on background
//
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^ {
//
//Load Game
//
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
CoreDataHelper *coreDataHelper = appDelegate.coreDataHelper;
NSManagedObjectContext *childMOC = [coreDataHelper createChildManagedObjectContext];
//the request
NSFetchRequest *fetchRequest = [NSFetchRequest new];
//the object entity we want
NSEntityDescription *entity = [NSEntityDescription entityForName:GAMEIMP_GAME inManagedObjectContext:childMOC];
[fetchRequest setEntity:entity];
//the predicate rules, the what
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"gameId == %#", #"1404110671234567"];
[fetchRequest setPredicate:predicate];
//the sorting rules
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc]initWithKey:GAMEIMP_OBJECT_ID ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc]initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
//Fetch results
NSFetchedResultsController *resultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:childMOC sectionNameKeyPath:nil cacheName:nil];
NSError *error;
BOOL success = [resultsController performFetch:&error];
GameImp *game;
if (success) {
game = [resultsController.fetchedObjects objectAtIndex:0];
} else {
NSLog(#"Unable to get game. Error: %#", error);
}
TurnImp *turnImp = game.turn;
//Issue is here!!! Should be 3, instead 0 because lastRoundReward is nil.
int lastRoundReward = [turnImp.lastRoundReward intValue];
//Work around, call custom getter method. Now 3 is returned.
lastRoundReward = [turnImp getLastRoundReward];
}
This childMOC creation
-(NSManagedObjectContext*) createChildManagedObjectContext {
NSManagedObjectContext *childMOC = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
childMOC.parentContext = self.mainManagedObjectContext;
return childMOC;
}
TurnImp Header
#interface TurnImp : Turn
#property(atomic) BOOL isValid;
- (void) setLastRoundReward: (int) lastRoundReward;
- (int) getLastRoundReward;
#end
TurnImp M
#implementation TurnImp
#synthesize isValid;
#synthesize lastRoundReward = _lastRoundReward;
/**
* Set the last round reward
* #param -
* #return -
*/
- (void) setLastRoundReward: (int) lastRoundReward {
_lastRoundReward = [NSNumber numberWithInt:lastRoundReward];
}
/**
* Get the int value of lastRoundReward
*/
- (int) getLastRoundReward {
//Note - HACK! Lazy loading not working, try another member
if (self.lastRoundReward == nil) {
//Force load
NSString *objectId = self.objectId;
}
return [self.lastRoundReward intValue];
}
Change childMoc to mainMoc and it works. MainMoc Code
//create the main MOC
_mainManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
More After Fixed Concurrency issue
[childMOC performBlock:^{
// Execute the fetch on the childMOC and do your other work.
NSError *error;
NSArray *results = [childMOC executeFetchRequest:fetchRequest error:&error];
if (results == nil) {
// Handle error
} else if (results.count == 1) {
GameImp *game = [results firstObject];
TurnImp *turnImp = game.turn;
//Issue is here!!! Should be 3, instead 0 because lastRoundReward is nil.
int lastRoundReward = [turnImp.lastRoundReward intValue];
//Work around, call variable objectId (not same as ObjectId)
NSString *objectId = turnImp.objectId;
//not it's 3...
lastRoundReward = [turnImp.lastRoundReward intValue];
}
}];
Work Around
I removed the following from TurnImp and it works as expected with the relationships.
#synthesize lastRoundReward = _lastRoundReward;
First, I have to confess that I have no idea what your problem statement means - what is lazy loading of a relationship supposed to do anyway?
However, a quick glance at your code reveals that you are creating a MOC with NSPrivateQueueConcurrencyType yet you are not properly wrapping its use inside an appropriate performBlock invocation.
When you clearly violate the Core Data Concurrency guidelines, you are playing in dangerous waters and will get undefined behavior.
Also, why create an instance of NSFetchedResultsController just to perform a fetch? That's overkill. Simply use a fetch request. Like so...
[childMOC performBlock:^{
// Execute the fetch on the childMOC and do your other work.
NSError *error;
NSArray *results = [childMOC executeFetchRequest:fetchRequest error:&error];
if (result == nil) {
// Handle error
} else if (results.count == 1) {
GameImp *game = [results firstObject];
TurnImp *turnImp = game.turn;
int lastRoundReward = [turn2.lastRoundReward intValue];
}
}];

How to update transformable attribute in core data

I am in a situation where i need to update transformable attribute in my entity in core data, until now i've tried every possible answer from google and stack overflow but did't achieve anything.
This is the method where i am saving object in core data, and my object which i am saving is an NSMutablDictionary type object.
-(void)didSaveToCoreData :(NSMutableDictionary *)newDict
{
#try {
AppDelegate *appDelegate = [[UIApplication sharedApplication]delegate];
NSManagedObjectContext *context = appDelegate.managedObjectContext ;
DataModelSupport *entity = [NSEntityDescription insertNewObjectForEntityForName:#"CPIEntity" inManagedObjectContext:context];
if (newDict != nil) {
[entity.fixed_Model removeAllObjects];
entity.fixed_Model = newDict;
}
NSError *error ;
[context save:&error];
if(error)
{
NSLog(#"Error in Saving Data");
}
else
{
[self didFetchFromCoreDataModel];
NSLog(#"Successfully saved");
}
}
#catch (NSException *exception) {
[self spareMeFromTheCrash:exception];
}
#finally {
}
}
in this method i am saving a dictionary object of 19 key/value, at the first time and i am fetching it correctly in didFetchFromCoreDataModel method, but when i refresh the data and get dictionary of 18 key/value i save that dictionary in core data using the same method didSaveToCoreData and fetch it in the same way from didFetchFromCoreDataModel but it still show 19 key/value
DataModelSupport is a subclass of NSManagedObject.
In DataModelSupport.h:
#property (nonatomic,weak) NSMutableDictionary *fixed_Model;
In DataModelSupport.m:
#dynamic fixed_Model;
This is it for the DataModelSupport class.
Now here in this method i am fetching the same object form core data
-(void)didFetchFromCoreDataModel
{
#try {
AppDelegate *appDelegate = [[UIApplication sharedApplication]delegate];
NSManagedObjectContext *context = appDelegate.managedObjectContext ;
NSEntityDescription *entity = [NSEntityDescription entityForName:#"CPIEntity" inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc]init];
[request setReturnsDistinctResults:YES];
[request setReturnsObjectsAsFaults:NO];
[request setResultType:NSDictionaryResultType];
[request setEntity:entity];
NSError *error ;
NSArray *arr = [context executeFetchRequest:request error:&error];
updatedfinalArr = [arr valueForKey:#"fixed_Model"];
if(error)
{
NSLog(#"Error");
}
else
{
}
}
#catch (NSException *exception) {
[self spareMeFromTheCrash:exception];
}
#finally {
}
}
And this is how my core data looks like:-
Any help is appreciated.
EDIT
I've implemented some changes in my code now in didSaveToCoreData method i am using this line of code to fetch the Entity by name
NSEntityDescription *descriptor = [NSEntityDescription entityForName:#"CPIEntity" inManagedObjectContext:context];
by this i am not creating new entity every time i call didSaveToCoreData method.
and this is how i am saving NSMutlableDictionary object
DataModelSupport *entity = [[DataModelSupport alloc]initWithEntity:descriptor insertIntoManagedObjectContext:context];
[entity.fixed_Model removeAllObjects]
entity.fixed_Model = newDict;
but still i am not getting correct result.
now when i refresh the data and save it using the above procedure explained in EDIT section, and fetch it, i get the updated data but it increase the number of objects, like on first attempt when i fetch i got 1 object in array, and on second attempt i got 2 objects and it goes like this, so when ever new data is added its not updating it but instead it add it in the entity s fixed_Model attribute and increase the number of object.
Lastly now i am using this line of code to get the last and update object from array in didFetchFromCoreDataModel method
NSDictionary *ddd = [[arr valueForKey:#"fixed_Model"]lastObject];
updatedfinalArr = [NSMutableArray arrayWithObject:ddd];
Your save method creates a new CPIEntity object each time. So, unless you delete the old object elsewhere in your code, I suspect your fetch is returning several objects, the first of which has the dictionary with 19 key/value pairs in the fixed_Model attribute, and the second/subsequent objects contain the 18 key/value pairs.
When you save, you should try to fetch the existing object first, and if you get zero results then create a new object. Then set the fixed_Model attribute of the new/existing object to your new dictionary.
EDIT
You are still inserting a new object each time (DataModelSupport *entity = [[DataModelSupport alloc]initWithEntity:descriptor insertIntoManagedObjectContext:context];). See below for an example of "fetch or create":
AppDelegate *appDelegate = [[UIApplication sharedApplication]delegate];
NSManagedObjectContext *context = appDelegate.managedObjectContext ;
NSEntityDescription *descriptor = [NSEntityDescription entityForName:#"CPIEntity" inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc]init];
request.entity = descriptor;
NSError *error;
NSArray *results = [context executeFetchRequest:request error:&error];
if (results == nil) {
// This implies an error has occurred.
NSLog(#"Error from Core Data: %#", error);
} else {
if (results.count == 0) {
// No objects saved, create a new one...
DataModelSupport *entity = [[DataModelSupport alloc]initWithEntity:descriptor insertIntoManagedObjectContext:context];
entity.fixed_Model = newDict;
} else {
// At least one object saved. There should be only one
// so use the first...
DataModelSupport *entity = [results firstObject];
entity.fixed_Model = newDict;
}
}
I've assumed for simplicity that newDict is not nil; amend as appropriate to handle that case.
Can you narrow down the problem?
Ie. can you compare the two Dictionaries..the original one with 19 values and the new one with 18 values?
Is there a particular entry which is not being 'removed'? That might point to a challenge with 'delete' (or the lack there of).
Alternatively, if you completely replace the content, what result do you get on fetch?

the object is not being turned into a fault

The problem randomly occurs...
Crashing Location (which is a method in NSOperationQueue)
[self.requestOperationQueue addOperationWithBlock: ^{
NSArray *titleList = [[NSMutableArray alloc] init];
NSArray *allBooks = [[CoreDataManager sharedInstance] fetchBooks];
for (Book *book in allBooks)
[titleList addObject:book.title]; // program crashed here!! failed to fault the value of book.title
}];
I use managedObjectContentChild for NSEntityDescription.
However, executeFetchRequest by managedObjectContent, which is the parent of managedObjectContentChild.
Is that the potential problem?
I tried executeFetchRequest by managedObjectContentChild, however, it leads many more different issues.
However, I am binded to use managedObjectContentChild since program is running in multiple threads by create new CoreDataManager instance for individual thread. Program will run into deadlock without using children MOC.
Thanks in advance!
CoreDataManager.m
- (id)init
{
if ((self = [super init]) != nil)
{
delegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
// Writer (write data to Persistent Store Coordinator)
writerManagedObjectContext = [delegate writerManagedObjectContext];
// Parent (Fetched Result Controller)
managedObjectContext = [delegate managedObjectContext];
// Child (handling Object Context Saving for individual threads)
managedObjectContextChild = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
managedObjectContextChild.parentContext = managedObjectContext;
bookEntity = [NSEntityDescription entityForName:[Book description]
inManagedObjectContext:managedObjectContextChild];
friendEntity = [NSEntityDescription entityForName:[Friend description]
inManagedObjectContext:managedObjectContextChild];
}
return self;
}
- (NSArray *)fetchBooks
{
// Todo: fix the problem of "CoreData: error: NULL _cd_rawData but the object is not being turned into a fault"
NSArray *results = nil;
if (key == nil)
return results;
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:key ascending:ascending];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entity];
[request setPredicate:predicate];
[request setSortDescriptors:sortDescriptors];
NSError *error = NULL;
// results = [managedObjectContextChild executeFetchRequest:request error:&error];
results = [managedObjectContext executeFetchRequest:request error:&error];
if (error != NULL)
NSLog(#"Error fetching - %#", error);
return results;
}
I tried to figure out the following discussion, but still have no clue how to do it...
CoreData: error: NULL _cd_rawData but the object is not being turned into a fault
http://www.cocoabuilder.com/archive/cocoa/311615-weird-core-data-crash.html
Here is the problem,
// use this one
results = [managedObjectContextChild executeFetchRequest:request error:&error];
// not this
// results = [managedObjectContext executeFetchRequest:request error:&error];
so I use managedObjectContextChild (child MOC) instead of managedObjectContext (parent MOC) in order to create distinct MOC for each individual threads. As a rules of concurrency of CoreData.
using managedObjectContext (parent MOC) will not cause the error of object not turning to fault and crashes the app every single time, but it's serious issue if the app happened to be using the same MOC (well, there is only one managedObjectContext in this case) at the exactly the same moment even from different threads.

CoreData NSManagedObject being corrupted

I have a controller that is the root of a workflow. If there is no data object for the workflow, then I create a new one, and if there is, I use the existing one. I have a property for the model object (an NSManagedObject)
#property (nonatomic, retain) Round *currentRound;
and I call the following whenever the corresponding view is shown
self.currentRound = [self.service findActiveRound];
if (!self.currentRound) {
NSLog((#"configure for new round"));
self.currentRound = [self.service createNewRound];
...
} else {
NSLog(#"configure for continue");
// bad data here
}
The problem is at the place marked in the above, sometimes the data object is corrupted. In the parts I didn't show I set the values on some text fields to represent the values in the model. Sometimes its ok, but eventually the properties on the model object are empty and things break
In the debugger, the reference to the round doesn't appear to change, but NSLogging the relevant properties shows them nullified. debugging seems to delay the onset of the corruption.
I am aware I am not saving the context...should that matter? And if so, how come it doesn't always fail the first time I come back to this controller?
My findActiveRound message is nothing special, but in case it matters
-(Round *) findActiveRound
{
NSLog(#"Create Active Round");
NSFetchRequest *request = [[NSFetchRequest alloc]init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Round" inManagedObjectContext:context];
[request setEntity:entity];
NSPredicate *pred = [NSPredicate predicateWithFormat:#"isComplete == %#", [NSNumber numberWithBool:NO]];
[request setPredicate:pred];
NSError *error = nil;
NSArray *results = [context executeFetchRequest:request error:&error];
if ([results count] == 0) {
return nil;
} else {
return [results objectAtIndex:0];
}
}
Many thanx.
EDIT FOR RESPONSE
By corrupted I mean when I try to get some simple string properties off the model object, I get nil values. So In the code above (where I think I have a round) I do stuff like
self.roundName.text = self.currentRound.venue;
self.teeSelection.text = self.currentRound.tees;
and don't see the data I entered. Since it only fails sometimes, but always fails eventually, I will see the data I entered for a while before its gone.
I'm pretty sure the context is the same. My service is a singleton and created like so
#implementation HscService
+(id) getInstance
{
static HscService *singleton = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
singleton = [[self alloc] init];
});
return singleton;
}
-(id) init
{
if (self = [super init]) {
model = [NSManagedObjectModel mergedModelFromBundles:nil];
NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
NSString *path = [self itemArchivePath];
NSURL *storeUrl = [NSURL fileURLWithPath:path];
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES],
NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES],
NSInferMappingModelAutomaticallyOption, nil];
NSError *error = nil;
if (![psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:options error:&error]) {
[NSException raise:#"Open failed" format: #"Reason: %#", [error localizedDescription]];
}
context = [[NSManagedObjectContext alloc] init];
[context setPersistentStoreCoordinator:psc];
[context setUndoManager:nil];
}
return self;
}
-(NSString *) itemArchivePath
{
NSArray *docDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *dir = [docDirectories objectAtIndex:0];
return [dir stringByAppendingPathComponent:#"hsc1.data"];
}
and in every controller I get the singleton to perform operations. I plan on defining a delegate around round operations, and implementing it in my AppDelegate, so I'm only getting the service once in the app, but don't think that should matter for now....
Are you sure the data is actually corrupted? Managed object contexts are highly efficient, and it's normal to fault in the debugger. From the docs:
"Faulting is a mechanism Core Data employs to reduce your
application’s memory usage..."
See http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CoreData/Articles/cdFaultingUniquing.html
If the data is actually missing and cannot be accessed by other methods, make sure you're using the same Managed Object Context to access the data. If the data has not been committed to the data store, it will not "sync" between MOCs.

Managing core data objects with a data manager singleton

I have a set of items in a plist. When my app starts, I read in the plist and save it as an array in my DataManager singleton, like this:
NSString *path = [[NSBundle mainBundle] bundlePath];
NSString *itemDatapath = [path stringByAppendingPathComponent:#"ItemData.plist"];
NSDictionary *itemData = [NSDictionary dictionaryWithContentsOfFile:itemDatapath];
dataManager.items = [itemData objectForKey:#"Items"];
I also want to store the core data objects that are associated with this data in the DataManger, so I attempted this:
-(void)setItems:(NSArray *)_items //causes EXC_BAD_ACCESS error
{
self.items = _items;
NSManagedObjectContext *context = [self managedObjectContext];
for (NSDictionary *item in self.items)
{
NSManagedObject *itemObject = [NSEntityDescription
insertNewObjectForEntityForName:#"Item"
inManagedObjectContext:context];
[itemObject setValue:[NSNumber numberWithInteger:[[item valueForKey:#"id"] intValue]] forKey:#"identifier"];
[itemObject setValue:[UIImage imageNamed:[item valueForKey:#"image"]] forKey:#"image"];
...
}
NSError *error;
if (![context save:&error]) {
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}
}
The point being that anywhere in my app I can access the objects from this method:
-(NSArray*)fetchItems
{
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"Item" inManagedObjectContext:managedObjectContext];
NSError *error2;
NSFetchRequest *itemFetchRequest = [[NSFetchRequest alloc] init];
[itemFetchRequest setEntity:entity];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"order"
ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[itemFetchRequest setSortDescriptors:sortDescriptors];
NSArray *fetchedItems = [managedObjectContext executeFetchRequest:itemFetchRequest error:&error2];
return fetchedItems;
}
The problem is the EXC_BAD_ACCESS error noted above. I would also like to know if there is a better way of going about this. I have the feeling storing the core data objects here is not the best practice. But even if I fetch the data when I need it in other view controllers, how can I manage updating the core data objects if they change? I have an external plist that may change, and the core data objects need to update based on that.
You are causing infinite recursion when you put self.items = _items inside the setItems: method. self.items is exactly the same as calling setItems - they invoke the same method. What you need to do instead is set the value of whatever your instance variable is - presumably items. So the first line of setItems: should be items = _items. That, in and of itself, is also confusing, as the convention is to have _ before variables indicate an instance variable.

Resources