Core Data app crashes on saving - objective-c

I got the following error [context save:&error]. How should I fix?
unrecognized selector sent to class
* -[NSKeyedArchiver dealloc]: warning: NSKeyedArchiver deallocated without having had -finishEncoding called on it.
* Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+[TempObject encodeWithCoder:]:
unrecognized selector sent to class'
Here the code I'm using.
NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
PostRequest *postRequestObject = (PostRequest *)[NSEntityDescription insertNewObjectForEntityForName:#"PostRequest" inManagedObjectContext:context];
DummyClass *request = OtherClassObject;
postRequestObject.request = request;
// request is attribute with type transformable
NSLog(#"requestpost data -> %#",postRequestObject.request);
NSError *error = nil;
if (![context save:&error]) {
// Replace this implementation with code to handle the error appropriately.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
#pragma mark - private method
- (NSFetchedResultsController *)fetchedResultsController
{
if (_fetchedResultsController != nil)
{
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[[NSFetchRequest alloc] init] autorelease];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:#"PostRequest" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
[fetchRequest setFetchBatchSize:80];
// Edit the sort key as appropriate.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"status" ascending:NO];
NSArray *sortDescriptors = #[sortDescriptor];
[sortDescriptor autorelease];
[fetchRequest setSortDescriptors:sortDescriptors];
NSFetchedResultsController *aFetchedResultsController = [[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:#"root"] autorelease];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return _fetchedResultsController;
}

The error message says that you have a class named TempObject, that you're trying to call encodeWithCoder: on it, and that this class does not implement that method. That's a crashing bug, as you're seeing.
It seems that you have a transformable Core Data attribute somewhere, and that you're assigning an instance of TempObject to this attribute. I don't know where that is. Your code snippet mentions a transformable attribute, but you don't use TempObject in the code. I can't tell if this code is not actually the cause of the crash or if you changed the name by hand for some reason.
Anyway, when you have a transformable attribute, you must do one of two things:
Make your class conform to NSCoding. You said in a comment that you did, but your error message says that you didn't-- or at least, that you didn't finish the job. NSCoding defines two methods, initWithCoder: and encodeWithCoder:, and your class must implement both of them.
Create a custom NSValueTransformer subclass that can convert your object to/from NSData, and configure your Core Data model to use this. Apple provides a couple of really good (but simple) examples of how to do this.
Anything you assign to a transformable attribute must do one of these. Which one is up to you.
It seems that you're trying to do #1 but haven't finished the job. Make sure that your class conforms to NSCoding.

Related

Mogenerator with NSFetchedResultsController - 0 sections

I am populating a UITableView from Core Data using an NSFetchedResultsController. The data is coming from an NSManagedObject subclass generated by Mogenerator called MenuItem. The MenuItem entity has a SectionID parameter that is an NSNumber and this is used to determine what section the item should be in within the table view.
I performed a test fetch on the data and confirmed that Core Data was populated correctly. All was fine.
The NSFetchedResultsController is created as follows and the sectionKeyNamePath is set to #"sectionId":
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"MenuItem" inManagedObjectContext:self.persistenceController.moc];
[fetchRequest setEntity:entity];
fetchRequest.sortDescriptors = #[[NSSortDescriptor sortDescriptorWithKey:#"sectionId" ascending:YES], [NSSortDescriptor sortDescriptorWithKey:#"rowId" ascending:YES]];
NSFetchedResultsController *frc = nil;
frc = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:self.persistenceController.moc
sectionNameKeyPath:#"sectionId"
cacheName:nil];
The problem (and solution):
With this code, the sections are not identified. The NSFetchedResultsController always returns 0. This worked previously when I created the NSMO subclass manually, so I figured it was something related to Mogenerator.
If I change the sectionNameKeyPath to be #"primitiveSectionId" then it works.
Hopefully this will help somebody in the future, but I don't understand what is going on here. Please could somebody explain why this fixes the problem?
Thanks
Why are you setting the FRC to nil? Try this (it works for me using an NSNumber Attribute):
With mogenerator you can get the entity using "[MenuItem entityName]" and access attributes using MenuItemAttributes.sectionId, for example.
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:[MenuItem entityName]];
fetchRequest.sortDescriptors = #[[NSSortDescriptor sortDescriptorWithKey:MenuItemAttributes.sectionId ascending:YES], [NSSortDescriptor sortDescriptorWithKey:MenuItemAttributes.rowID ascending:YES]];
NSFetchedResultsController *frc = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:self.persistenceController.moc
sectionNameKeyPath:MenuItemAttributes.sectionId
cacheName:nil];
// Make sure you set your frc as the delegate
frc.delegate = self;
NSError *error = nil;
if (![fetcher performFetch:&error]) {
/*
* Did you execute the request?
*/
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
NSLog(#"%lu", [frc.sections count]);

CoreData performFetch in viewDidLoad not working

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.

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.

Objective-C: Why this function name caused a warning?

This is the original header file:
#interface TestDataHelper : NSObject {
}
+(void) populateTestData:(NSManagedObjectContext*) managedObjectContext;
+(void) testPopulateTestData:(NSManagedObjectContext*) managedObjectContext;
#end
When I compile this file, I got this warning:
method definiton not found
for testPopulateTestData
When I ignore the warning and run the app in iphone simulator, I got a runtime exception:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+[TestDataHelper testPopulateTestData:]: unrecognized selector sent to class 0x104d8'
Rename the method to 'test' alone seems to solve the problem
What is special about testXXX method name?
EDIT: implementation is there and done. Renaming the method name (in both .h and .m) removes the warning, and the final app works.
EDIT 2:
Here is the implementation of the function test (originally named as testPopulatedTestData):
+(void) test:(NSManagedObjectContext*) managedObjectContext {
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Stock" inManagedObjectContext:managedObjectContext];
[request setEntity:entity];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[request setSortDescriptors:sortDescriptors];
[sortDescriptors release];
[sortDescriptor release];
NSError *error = nil;
NSMutableArray *mutableFetchResults = [[managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
NSEnumerator *e = [mutableFetchResults objectEnumerator];
id object;
while (object = [e nextObject]) {
// do something with object
Stock* fc = (Stock*) object;
NSLog(#"get a fc %s", [[fc name] description]);
}
}
The error message is telling you that you've declared a method in your header file that you never implemented anywhere. The runtime error is telling you the same thing - you've sent the selector testPopulateTestData to the class TestDataHelper but it was never implemented.

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