CoreData performFetch in viewDidLoad not working - objective-c

When my main view controller is loaded and it calls viewDidLoad I am performing a fetch request and retiring an array or my core data objects:
+ (NSArray *)getData {
// Fetch Data
NSError *error = nil;
if (![[[AppDelegate instance] fetchedResultsController] performFetch:&error]) {
// Update to handle the error appropriately.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
NSArray *items = [[AppDelegate instance].fetchedResultsController fetchedObjects];
return items;
}//end
For some reason, this returns an empty array when called from viewDidLoad.
If I call the same method from viewDidAppear: it works correctly and returns the NSArray of my CoreData objects.
Is there a reason why this won't work in viewDidLoad?
EDIT:
Fetched Results Controller method:
/**
* The controller the gets our results from core data
*
* #version $Revision: 0.1
*/
- (NSFetchedResultsController *)fetchedResultsController {
if (fetchedResultsController != nil) {
return fetchedResultsController;
}
// Create and configure a fetch request
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"SiteConfig" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
// Create the sort descriptors array
NSSortDescriptor *sectionTitle = [[NSSortDescriptor alloc] initWithKey:#"createdDate" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sectionTitle, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
// Create and initialize the fetch results controller
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:#"Root"];
self.fetchedResultsController = aFetchedResultsController;
fetchedResultsController.delegate = self;
// Memory management
return fetchedResultsController;
}//end

My guess is that your view controller is part of your MainWindow.xib, and so its viewDidLoad is being called before your app delegate gets the core data context ready.
You probably want to run this in viewWillAppear anyway, so that you'll get new data if you leave the screen and return. The other option would be to ensure the app delegate responds to fetchedResultsController by getting the core data stack ready if it isn't already.

Related

controllerWillChangeContent not being called when deleting all entries of an entity

Below is the method I have implemented for a tableView "reset" button. I've verified that the entries in the Entity are being deleted, however my controllerWillChangeContent is not being called after the deletion. Is there a way to call this method manually?
CoreDataStack *coreDataStack = [[CoreDataStack alloc]init];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"FoodEntry"];
[fetchRequest setIncludesPropertyValues:NO];
NSError *error;
NSArray *fetchedObjects = [coreDataStack.managedObjectContext executeFetchRequest:fetchRequest error:&error];
for (NSManagedObject *object in fetchedObjects)
{
[coreDataStack.managedObjectContext deleteObject:object];
}
error = nil;
[coreDataStack.managedObjectContext save:&error];
EDIT : FRC CODE
- (NSFetchedResultsController *)fetchedResultsController {
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
CoreDataStack *coreDataStack = [CoreDataStack defaultStack];
NSFetchRequest *fetchRequest = [self entryListFetchRequest];
_fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:coreDataStack.managedObjectContext sectionNameKeyPath:nil cacheName:nil];
_fetchedResultsController.delegate = self;
return _fetchedResultsController;
}
Can't be certain, without knowing the internals of CoreDataStack, but it looks like you are using two different stacks: notice that your code uses:
CoreDataStack *coreDataStack = [[CoreDataStack alloc] init];
whereas the FRC uses:
CoreDataStack *coreDataStack = [CoreDataStack defaultStack];
You could change your code to use the same as the FRC (defaultStack is probably a singleton and will return the same stack whenever it is called). But if the FRC is in the same class as the delete code, it will be easier (and clearer) to just use the FRC's managedObjectContext:
NSManagedObjectContext *context = self.fetchedResultsController.managedObjectContext
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"FoodEntry"];
[fetchRequest setIncludesPropertyValues:NO];
NSError *error;
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
for (NSManagedObject *object in fetchedObjects)
{
[context deleteObject:object];
}
error = nil;
[context save:&error];
Incidentally, it's always worth checking if the executeFetchRequest: and save: return an error.

Can not read data using NSManagedObjectContext

I am trying to read an array from Core Data. My code is as follows
-(NSArray *)getAges
{
NSArray *fetchedObjects = [[NSArray alloc] init];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSManagedObjectContext *context = nil;
context = [[NSManagedObjectContext alloc] init];
#try {
NSPersistentStoreCoordinator *tempCordinator = (NSPersistentStoreCoordinator *)[(AppDelegate *)[[UIApplication sharedApplication] delegate] persistentStoreCoordinator];
[context setPersistentStoreCoordinator:tempCordinator];
NSError *error;
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Age"
inManagedObjectContext:context];
[fetchRequest setEntity:entity];
fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
}
#catch (NSException *exception) {
NSLog(#"core data exception occured.");
fetchedObjects = nil;
}
[fetchRequest release];
[context release];
return fetchedObjects;
}
Now in main thread, in an IBAction method, I write following line.
[NSThread detachNewThreadSelector:#selector(loadAgesMethod) toTarget:self withObject:nil];
Problem is that this method returns me an array of 20 elements. I can see the array count 20 but in debugger, against every element of array, i see 'out of scope' and 'summary unavailable'. And when I try to fetch any object from this array, i get nothing. One thing is to mention is that I am calling this method getAges in loadAgesMethod from a secondary thread as I mentioned above. This is my requirement. Also when I try to retrieve this array from main thread, i get all data perfectly ok.
Can anyone please give me a hint, where I am wrong?
Best regards
Every NSManagedObject has a NSManagedObjectContext property. This is used to lazily fetch properties of the object when required in the future. At the end of this method you call [context release] hence the NSManagedObjectContext will be deallocated and all your NSManagedObject context properties will be nil for your Age objects.
You could call [context autorelease] on the context so that it persists for some time after the method and allows you to fetch properties on the objects at a later stage. Note this will only persist until the autorelease pool drains at the end of the run loop. I am still trying myself to come up with a better solution to multithreaded fetching.

Core Data: Fetched Results Controller causing over release/crash

I am using core data, and have a UITableViewController which loads the data from core data. This is a modal view and if I load the modal view for the third time, it crashes with EXC_BAD_ACCESS.
- (NSFetchedResultsController *)fetchedResultsController {
if (fetchedResultsController != nil) {
return fetchedResultsController;
}
// Create and configure a fetch request
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Ride" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
// Create the sort descriptors array
NSSortDescriptor *sectionTitle = [[NSSortDescriptor alloc] initWithKey:#"dateStart" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sectionTitle, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
// Create and initialize the fetch results controller
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:nil cacheName:#"Root"];
self.fetchedResultsController = aFetchedResultsController;
fetchedResultsController.delegate = self;
// Memory management
[aFetchedResultsController release];
[fetchRequest release];
[sectionTitle release];
[sortDescriptors release];
return fetchedResultsController;
}//end
The crash says that it is coming from this line:
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Ride" inManagedObjectContext:managedObjectContext];
This is in viewDidLoad:
if (managedObjectContext == nil) {
managedObjectContext = [MyAppDelegate instance].managedObjectContext;
}
Presenting the modal view:
History *history = [[[History alloc] init] autorelease];
UINavigationController *nav = [[[UINavigationController alloc] initWithRootViewController:history] autorelease];
[self presentModalViewController:nav animated:YES];
Dismissing Modal:
- (void)done {
[self dismissModalViewControllerAnimated:YES];
}
Stack Trace that gives EXC_BAD_ACCESS:
Now, in order to setup this view with core data, I followed the Core Data Books example project and my code looks almost identical. Why would it be crashing after a few times of loading the modal view?
Ok, I figured it out, my managedObjectContext was not being retained because I was not using self. So, I fixed it by doing:
// Core Data
if (managedObjectContext == nil) {
self.managedObjectContext = [MyAppDelegate instance].managedObjectContext;
}

Objective C - NSManagedObjectContext and NSFetchedResultsController release handling

i need some information about memory management with NSManagedObjectContext objects.
I`m programming on an ipad-App and i work with core data objects.
My UIApplicationDelegate controls the NSManagedObjectContext:
- (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 using instances of the ManagedObjectContext to display my Core Data Objects.
like this.
example table view controller:
CRMAppDelegate *appDelegate = (CRMAppDelegate*) [[UIApplication sharedApplication] delegate];
self.managedObjectContext = appDelegate.managedObjectContext;
// Initialize NSFetchedResultsController to retrieve data from the database.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:[NSEntityDescription entityForName:#"Contact" inManagedObjectContext:self.managedObjectContext]];
// Sort results ascending by last name, first name.
NSSortDescriptor *sortDescriptorFirstLetterLastName = [[NSSortDescriptor alloc] initWithKey:#"firstLetterOfLastName" ascending:YES];
NSSortDescriptor *sortDescriptorLastName = [[NSSortDescriptor alloc] initWithKey:#"name_last" ascending:YES];
NSSortDescriptor *sortDescriptorFirstName = [[NSSortDescriptor alloc] initWithKey:#"name_first" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptorFirstLetterLastName, sortDescriptorLastName, sortDescriptorFirstName, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
[sortDescriptors release];
[sortDescriptorFirstLetterLastName release];
[sortDescriptorFirstName release];
[sortDescriptorLastName release];
/*
if ([self.managedObjectContext countForFetchRequest:fetchRequest error:NULL] == 0) {
[self reloadContactData];
}
*/
// Create NSFetched Results controller instance.
fetchedResultsController = [[NSFetchedResultsController alloc]
initWithFetchRequest:fetchRequest
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:#"firstLetterOfLastName"
cacheName:#"ContactsViewCache"];
[fetchRequest release];
self.fetchedResultsController.delegate = self;
// Load data.
NSError *error;
if (![fetchedResultsController performFetch:&error]) {
// Update to handle the error appropriately.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
Is it ok to instance the NSManagedObjectContext like this? Because, the retainCount of the context is increasing til 10 while i`m testing my APP, even though i'm releasing the the context in "viewDidUnload" and "dealloc".
I`ve tried retain the context in a handler class like this:
- (NSManagedObjectContext *)getManagedObjectContext
{
NSManagedObjectContext *context = [self.managedObjectContext autorelease];
return context;
}
Whats the best way to interact with NSManagedObjectContext - objects and instances?
Thx and sry for my stuoid question :)
The value returned by retainCount is meaningless. Don't bother looking at it.
Use the Leaks instrument or heapshot analysis to determine if your app is leaking.

NSFetchedResultsController always returns NSManagedObject objects instead of custom

I am using CoreData and have an Entity ContactList defined, which I created using the XCode xcdatamodel tool (or whatever you call that built-in thing:). I also set the Entity's Class name to "ContactList". I then used rentzsch's mogenerator (http://github.com/rentzsch/mogenerator) to generate my custom class files and added the simple method "toString" to my ContactList class.
The fetchedResultsController callback is pretty much standard and looks as follows:
- (NSFetchedResultsController *)fetchedResultsController {
if (fetchedResultsController_ != nil) {
return fetchedResultsController_;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"ContactList" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
[fetchRequest setFetchBatchSize:20];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"timeStamp" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:#"Root"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
[aFetchedResultsController release];
[fetchRequest release];
[sortDescriptor release];
[sortDescriptors release];
NSError *error = nil;
if (![fetchedResultsController_ performFetch:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return fetchedResultsController_;
}
Now, when I start my application and try to call that method on a "ContactList" object, which in turn I get from a NSFetchedResultsController, I get
ContactList *contactList = (ContactList *) [self.fetchedResultsController objectAtIndexPath:indexPath];
[contactList toString];
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSManagedObject toString]: unrecognized selector sent to instance 0x6af7bf0'
As the message shows, somehow the NSFetchedResultsController does not return a "ContactList" object, but a more general NSManagedObject instead.
Does anybody know why and how to resolve this???
I had this same problem - Caused because I had previously renamed the Entity's Managed Object subclass. In the Data Modeler, make sure in the Inspector for the entity, both the "Name" and the "Class" are set correctly.
Ok, I figured this it out.
The problem was that the mogenerator script did not add its generated source files to the project's compile target. This way the classes never got compiled and included (I wonder how it worked without any model object classes :)
After adding the .xcdatamodel file to the Target (RightClick on .xcdatamodel file -> Targets -> check your project) everything works now.
For further info go here