Should NSFetchRequest be explicitly performed on context queue? - core-data

Just a little question...
When performing CoreData requests (in objective-C), do I have to make them on the context queue explicitly, or are they assumed to do that by themselves?
In other words can I just write:
NSArray *results = [context executeFetchRequest:request error:nil];
Or do I have to write (if I want to avoid issues):
__block NSArray *results = nil;
[context performBlockAndWait:^{
results = [context executeFetchRequest:request error:nil];
}];
Thanks!

You can never assume that Core Data will use the correct queue. You always need to tell it via one of the “perform” methods. The only exception is if you have a main-queue context and you know your code is running on the main queue. You can skip “perform” in that one and only case.
You mention using a convenience method to do a fetch, using “perform” inside the method. That’s fine, but don’t forget that you also need to use “perform” any time you use any of the fetched objects. For example, you can’t use property values from fetched objects unless you also use “perform” (again with the one exception I mentioned).

So just to be sure Tom:
All my CoreData objects (subclasses of NSManagedObject) have their CoreData properties as private (with a prefix "cd_"). I never expose them and use wrapper properties instead:
- (nullable NSString *) email {
return self.cd_email;
}
I've created a method on NSManagedObject:
- (id) safeValueForProperty: (SEL)property {
__block id value = nil;
[self.managedObjectContext performBlockAndWait:^{
value = [self valueForKey:NSStringFromSelector(property)];
}];
return value;
}
So now I can use (from any thread in theory):
- (nullable NSString *) email {
return [self safeValueForProperty:#selector(cd_email)];
}
(and of course the same for setters).
Am I doing right?

Related

NSManagedObjectContext - Child Context causing deadlock

I have a parent - child - grandchild core data context setup in Core Data as below. Whenever I try execute a fetch request on the grandchild context, it causes a deadlock on the thread
- (NSManagedObjectContext *)defaultPrivateQueueContext
{
if (!_defaultPrivateQueueContext) {
_defaultPrivateQueueContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
_defaultPrivateQueueContext.persistentStoreCoordinator = self.persistentStoreCoordinator;
}
return _defaultPrivateQueueContext;
}
- (NSManagedObjectContext *)mainThreadContext {
if (!_mainThreadContext) {
_mainThreadContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
_mainThreadContext.parentContext = [self defaultPrivateQueueContext];
}
return _mainThreadContext;
}
+ (NSManagedObjectContext *)newPrivateQueueContext
{
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
context.parentContext = [[self sharedParliamentAPI] mainThreadContext];
return context;
}
This is my code where it causes a deadlock (when trying to execute a fetch request):
- (void)fetchMenuItemsWithCompletion:(void (^) (BOOL success, NSString *message))completionBlock {
NSMutableURLRequest *request = [APIHelper createNewRequestWithURLExtension:#"menuitems" httpMethodType:#"GET" parameters:nil];
NSURLSession *session = [NSURLSession sessionWithConfiguration:self.sessionConfig];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSObject *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
if ([[json valueForKey:#"isSuccess"] boolValue]) {
NSManagedObjectContext *defaultContext = self.defaultPrivateQueueContext;
NSManagedObjectContext *privateQueueContext = [ParliamentAPI newPrivateQueueContext];
[privateQueueContext performBlock:^{
__block NSError *error;
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"MenuItem"];
NSArray *fetchedRecords = [privateQueueContext executeFetchRequest:request error:&error];
// do stuff with fetchedRecords
}];
} else {
completionBlock([[json valueForKey:#"isSuccess"] boolValue], [json valueForKey:#"message"]);
}
}];
[dataTask resume];
}
Your Core Data object structure looks like this:
Where you have a NSPrivateQueueConcurrencyType context as the root context connected to the persistent store coordinator, which has a child NSMainQueueConcurrencyType context, which in turn has a NSPrivateQueueConcurrencyType context. This structure is recommended by a lot of people who write on The IntarWebs.
What is happening in your case is that the private queue context that is the main queue context is becoming busy, which is causing it's child to wait. Because the main queue context is using the main queue for all of it's work, it's not necessarily busy doing Core Data work when this happens (though this is still somewhat likely). The main queue does a lot of work other than Core Data, and all of those things will end up impacting the child any time it needs to communicate with the main queue context.
Additionally, a context created with NSMainQueueConcurrencyType is allowed to execute Core Data operations without explicitly using performBlock: or performBlockAndWait:.
For example, a main queue context can do this:
NSArray *results = nil;
NSError *error = nil;
results = [mainQueueContext executeFetchRequest:fetchRequest error:&error];
And is not required to do this:
[mainQueueContext performBlock:^{
NSArray *results = nil;
NSError *error = nil;
results = [mainQueueContext executeFetchRequest:fetchRequest error:&error];
}];
The difference here is that the first example without the performBlock: will block the main thread waiting for the result. The second, using performBlock:, is asynchronous and will not block - the fetch request will be scheduled for execution on the queue. Obviously, the first example can cause some contention in any child contexts.
If your configuration had a NSMainQueueConcurrencyType context that was a child of another NSMainQueueConcurrencyType context, that would be... bad. It's almost guaranteed to deadlock in that configuration. The good news is, you don't have that issue!
You can convert your code to using performBlock: with the NSMainQueueConcurrencyType context to mitigate this part of the problem. You can also use an NSPrivateQueueConcurrencyType in place of your main queue context - there is not much of a good reason to use a main queue context at all. NSFetchedResultsController can be used with a private queue context to do "background fetching".

Initialising NSManagedObjectContext on a serial NSOperationQueue

I have an NSOperationQueue that has some NSBlockOperations added to it, among which are blockOperations A and B. The NSOperationQueue has a maxConcurrencyOperationCount of 1.
blockOperation B, is dependant on A being finished. In each of the blockOperations I am calling a method, which in turn calls another method, that initialises a new NSManagedObjectContext (with the persistentStoreCoordinator from a singleton), that I use to create and add objects to a Core Data database. The code invoked by the aforementioned second method call in A and B looks like this (it varies slightly for for each of them):
NSManagedObjectContext *managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
managedObjectContext.persistentStoreCoordinator = [[CoreDataController sharedCoreDataController] persistantStoreCoordinator];
for (NSDictionary *articleDictionary in array) {
if (![Article articleExistsWithIDInDictionary:articleDictionary inContext:managedObjectContext]) {
[Article articleFromDictionary:articleDictionary inContext:managedObjectContext];
}
}
[[CoreDataController sharedCoreDataController] saveContext:managedObjectContext];
// method ends.
The saveContext: code looks like this:
NSError *error = nil;
if (managedObjectContext != nil) {
if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
}
Having spent a lot of time reading Apples Docs about Core Data Concurrency, NSOperation, etc., I'm still unsure if what I'm doing with NSManagedObjectContext is thread-safe, and generally considered to be OK? Some clarification and/or indication of that I should be doing differently would be much appreciated. If you need to see any more code, please ask.
Thanks in advance.
What you are doing is NOT thread safe.
If you decide to create a context per operation, you better use the confinement concurrency type:
context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSConfinementConcurrencyType];
this way you don't need to change anything in your current code.
if you want to use the context with NSPrivateQueueConcurrencyType you must access objects within that context by the performBlock: or performBlockAndWait::
[managedObjectContext performBlockAndWait:^{//wait is used as to not end the operation before this code is executed
for (NSDictionary *articleDictionary in array) {
if (![Article articleExistsWithIDInDictionary:articleDictionary
inContext:managedObjectContext])
{
[Article articleFromDictionary:articleDictionary
inContext:managedObjectContext];
}
}
}];
I would probably go with my first solution in your case.
all that said, you could simply use a "private queue" context as a serial queue (as long as you add block operation to it in the order you need them to execute).
A context performBlock: method will queue the block and execute it serially in regard to other blocks added that context for execution in the background:
//add this to your CoreDataController
context = [[CoreDataController sharedCoreDataController] serialExecutionBGContext];
[context performBlock:^{ //your block operation code1}];
[context performBlock:^{ //your block operation code2}];
this will perform code1 and code2 in the background serially.
In this manner, you save the overhead of allocating a new context, and might benefit from caching done by this context.
You might want to reset this context every now and then so it will not get bloated with fetched objects.
The concern with the context is that it be accessed only within a single thread. Setting the MaxConcurrencyOperationCount does not guarantee that. Another approach is to make the context a "thread" variable, storing a context in each thread dictionary where it is used.
Ex:
+ (NSManagedObjectContext*)managedObjectContext
{
NSMutableDictionary *threadDictionary = [[NSThread currentThread] threadDictionary];
NSManagedObjectContext *context = [threadDictionary valueForKey:#"QpyManagedObjectContext"];
if (context == nil) {
#autoreleasepool {
context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSConfinementConcurrencyType];
[context setStalenessInterval: 10.0];
[context setMergePolicy:[[NSMergePolicy alloc]initWithMergeType:NSOverwriteMergePolicyType]];
NSPersistentStoreCoordinator *coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[Qpyd managedObjectModel]];
[context setPersistentStoreCoordinator:coordinator];
NSString *STORE_TYPE = NSSQLiteStoreType;
NSString *path = [[NSProcessInfo processInfo] arguments][0];
path = [path stringByDeletingPathExtension];
NSURL *url = [NSURL fileURLWithPath:[path stringByAppendingPathExtension:#"sqlite"]];
NSError *error;
NSPersistentStore *newStore = [coordinator addPersistentStoreWithType:STORE_TYPE configuration:nil URL:url options:nil error:&error];
if (newStore == nil) {
NSLog(#"Store Configuration Failure %#", ([error localizedDescription] != nil) ? [error localizedDescription] : #"Unknown Error");
}
[threadDictionary setObject:context forKey:#"QpyManagedObjectContext"];
}
}
return context;
}

NSFetchedResultsController: NSSortDescriptor with relation as key sends unrecognized selector compare: to core data entity object

I am trying to save a one-to-many relation in core data. There is a user decision involved to determine whether the new child list object needs to be attached to a new parent object. In the other case an existing database entry is used as a parent object. Under certain circumstances after saving, the app crashes.
FINAL EDIT: Sorry if you mind me keeping all of the edits, I still will. The process of enlightenment was quite convoluted. After all I started out thinking it was a data conflict... Thanks again to Tom, who helped point me in the right direction: I am still using a relation for sorting and grouping core data entities with an NSFetchedResultsController. I have written a valid compare: method for my entity class now and so far from what I can see it is working. I am about to write an answer for my question. I will still be very grateful for any information or warnings from you concerning this!
EDIT 3: The save procedure and the user alert seem to be incidental to the problem. I have zoomed in on the NSFetchedResultsController now and on the fact that I am using a relation ('examination') as sectionNameKeyPath. I will now try to write a compare: method in a category to my Examination entity class. If that does not work either, I will have to write a comparable value into my Image entity class in addition to the relation and use that for sections. Are y'all agreed?
EDIT 1: The crash only occurs after the user has been asked whether she wants a new examination and has answered YES. The same method is also entered when there was no user prompt (when the creation of a new examination has been decided by facts (no examination existing = YES, existing examination not timed-out = NO). In these cases the error does NOT occur. It must be that the view finishes loading while the alert view is open and then the collection view and its NSFetchedResultsController join the fun.
EDIT 2: Thanks to Tom, here is the call stack. I did not think it was relevant, but the view controller displays images in a collection view with sections of images per examination descending. So both the section key and the sort descriptor of the NSFetchedResultsController are using the examination after the MOCs change notification is sent. It is not the save that crashes my app: it is the NSSortDescriptor (or, to be fair, my way to use all of this).
The code for the NSFetchedResultsController:
#pragma mark - NSFetchedResultsController
- (NSFetchedResultsController *)fetchedResultsController
{
if (m_fetchedResultsController != nil) {
return m_fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:NSStringFromClass([Image class]) inManagedObjectContext:[[HLSModelManager currentModelManager] managedObjectContext]];
[fetchRequest setEntity:entity];
// Set the batch size to a suitable number.
[fetchRequest setFetchBatchSize:20];
// Edit the sort key, identical sort to section key path must be first criterion
NSSortDescriptor *examinationSortDescriptor = [[NSSortDescriptor alloc] initWithKey:kexaminationSortDescriptor ascending:NO];
NSSortDescriptor *editDateSortDescriptor = [[NSSortDescriptor alloc] initWithKey:keditDateSortDescriptor ascending:NO];
NSArray *sortDescriptors =[[NSArray alloc] initWithObjects:examinationSortDescriptor, editDateSortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
// Edit the section name key path and cache name if appropriate.
// nil for section name key path means "no sections".
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:[[HLSModelManager currentModelManager] managedObjectContext] sectionNameKeyPath:kSectionNameKeyPath cacheName:NSStringFromClass([Image class])];
aFetchedResultsController.delegate = self;
m_fetchedResultsController = aFetchedResultsController;
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error]) {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
HLSLoggerFatal(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return m_fetchedResultsController;
}
#pragma mark - NSFetchedResultsControllerDelegate - optional
/* Asks the delegate to return the corresponding section index entry for a given section name. Does not enable NSFetchedResultsController change tracking.
If this method isn't implemented by the delegate, the default implementation returns the capitalized first letter of the section name (seee NSFetchedResultsController sectionIndexTitleForSectionName:)
Only needed if a section index is used.
*/
- (NSString *)controller:(NSFetchedResultsController *)controller sectionIndexTitleForSectionName:(NSString *)sectionName
{
return sectionName;
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
// In the simplest, most efficient, case, reload the table view.
[[self collectionView] reloadData];
}
/* THE OTHER DELEGATE METHODS ARE ONLY FOR UITableView! */
The code for saving examination (existing or new) and the new image:
-(BOOL)saveNewImage
{
BOOL done = NO;
// remove observer for notification after alert
[[NSNotificationCenter defaultCenter] removeObserver:self name:kExaminationTimedoutAlertDone object:nil];
Examination * currentExamination = [self getCurrentExamination];
if ([self userWantsNewExamination] == YES)
{ // if an examination was found
if (currentExamination != nil)
{ // if the found examination is not closed yet
if ([currentExamination endDate] == nil)
{ // close examination & save!
[currentExamination closeExamination];
NSError *savingError = nil;
[HLSModelManager saveCurrentModelContext:(&savingError)];
if (savingError != nil)
{
HLSLoggerFatal(#"Failed to save old, closed examination: %#, %#", savingError, [savingError userInfo]);
return NO;
}
}
}
currentExamination = nil;
}
// the examination to be saved, either new or old
Examination * theExamination = nil;
// now, whether user wants new examination or no current examination was found - new examination will be created
if (currentExamination == nil)
{
// create new examination
theExamination = [Examination insert];
if (theExamination == nil)
{
HLSLoggerFatal(#"Failed to create new examination object.");
currentExamination = nil;
return NO;
}
// set new examinations data
[theExamination setStartDate: [NSDate date]];
}
else
{
theExamination = currentExamination;
}
if (theExamination == nil)
{ // no image without examination!
HLSLoggerFatal(#"No valid examination object.");
return NO;
}
Image *newImage = [Image insert];
if (newImage != nil)
{
// get users last name from app delegate
AppDelegate * myAppDelegate = (AppDelegate *) [[UIApplication sharedApplication] delegate];
// set image data
[newImage setEditUser: [[myAppDelegate user] lastName]];
[newImage setEditDate: [NSDate date]];
[newImage setExamination: theExamination];
[newImage setImage: [self stillImage]];
[newImage createImageThumbnail];
// update edit data
[theExamination setEditUser: [[myAppDelegate user] lastName]];
[theExamination setEditDate: [NSDate date]];
// unnecessary! CoreData does it automatically! [theExamination addImagesObject:newImage];
//! Important: save all changes in one go!
NSError *savingError = nil;
[HLSModelManager saveCurrentModelContext:(&savingError)];
if (savingError != nil)
{
HLSLoggerFatal(#"Failed to save new image + the examination: %#, %#", savingError, [savingError userInfo]);
}
else
{
// reload data into table view
[[self collectionView] reloadData];
return YES;
}
}
else
{
HLSLoggerFatal(#"Failed to create new image object.");
return NO;
}
return done;
}
The error:
2013-05-22 17:03:48.803 MyApp[11410:907] -[Examination compare:]: unrecognized selector sent to instance 0x1e5e73b0
2013-05-22 17:03:48.809 MyApp[11410:907] CoreData: error: Serious application error. Exception was caught during Core Data change processing. This is usually a bug within an observer of NSManagedObjectContextObjectsDidChangeNotification. -[Examination compare:]: unrecognized selector sent to instance 0x1e5e73b0 with userInfo (null)
2013-05-22 17:03:48.828 MyApp[11410:907] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Examination compare:]: unrecognized selector sent to instance 0x1e5e73b0'
And here are the entity class files, too:
//
// Examination.h
// MyApp
//
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#class Image;
#interface Examination : NSManagedObject
#property (nonatomic, retain) NSDate * editDate;
#property (nonatomic, retain) NSString * editUser;
#property (nonatomic, retain) NSDate * endDate;
#property (nonatomic, retain) NSDate * startDate;
#property (nonatomic, retain) NSSet *images;
#end
#interface Examination (CoreDataGeneratedAccessors)
- (void)addImagesObject:(Image *)value;
- (void)removeImagesObject:(Image *)value;
- (void)addImages:(NSSet *)values;
- (void)removeImages:(NSSet *)values;
#end
//
// Examination.m
// MyApp
//
#import "Examination.h"
#import "Image.h"
#implementation Examination
#dynamic editDate;
#dynamic editUser;
#dynamic endDate;
#dynamic startDate;
#dynamic images;
#end
This error had nothing to do with the saving of data to the MOC.
Because the saving of the new image data is triggered in the prepareForSegue of the previous view controller and the user alert gives the next view controller the time to finish loading, also creating the NSFetchedResultsController and its connection to its delegate, the exception was raised in the temporary context of the save to the MOC and only after the user alert.
The NSFetchedResultsController had started listening for changes of the MOC only in this case. It seems that if it gets alerted to an MOC change it will fetch only the changes and only then it needs to compare the new data with the existing data. Further information on this would be very welcome!
Then, because I had set a sort descriptor (and also the sectionNameKeyPath) to a relation and not provided the means to sort the entity objects in my core data entity class, the NSFetchedResultsController could not continue. Looking back it seems all so easy and natural, I really become suspicious of the simplicity of my solution...
I find it interesting that it could fetch the initial data in one go, when no change interfered. After all it was using the same NSSortDescriptor. Any ideas?
This is my solution:
//
// MyCategoryExamination.m
// MyApp
//
#import "MyCategoryExamination.h"
#implementation Examination (MyCategoryExamination)
- (NSComparisonResult)compare:(Examination *)anotherExamination;
{
return [[self startDate] compare:[anotherExamination startDate]];
}
#end
Please tell me if there is something wrong with this.

Core data max value for property of entities children

I know I shouldn't look at Core Data as something relational. But with a SQL query it is easiest to explain what I'm trying to do.
What I'd like to do is get the max value from a property constrained to the children of an entity. In SQL something like: select max(valueY) from graphdata where parentID = 10
I'm doing something akin to the above SQL query successfully excluding the where (NSPredicate) part.
I added the NSPredicate to my code thinking it would exclude the graphdata objects not within the KPI I'm looking at. Well, guess not, it's not what I'm seeing, it seems to do nothing at all really.
The Core Data Model consistst of multiple KPI objects, each containing an Array of GraphData objects. Every GraphData object contains a valueX and a valueY property.
I want the max valueY for the graphdata of a specified KPI object.
Here's my code, it's based on one of Apple's core data snippets:
NSManagedObjectContext *context = ...
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:GRAPH_DATA_ENTITY_NAME inManagedObjectContext:context];
[request setEntity:entity];
// Specify that the request should return dictionaries.
[request setResultType:NSDictionaryResultType];
// Create an expression for the key path.
NSExpression *keyPathExpression = [NSExpression expressionForKeyPath:#"ValueY"];
//MY WHERE KIND OF CLAUSE/PREDICATE WHICH ISN'T WORKING?
NSPredicate *predicate =
[NSPredicate predicateWithFormat:#"kpiForGraphData == %#", self.kpi];
[request setPredicate:predicate];
// Create an expression to represent the function you want to apply
NSExpression *expression = [NSExpression expressionForFunction:#"max:"
arguments:[NSArray arrayWithObject:keyPathExpression]];
// Create an expression description using the minExpression and returning a date.
NSExpressionDescription *expressionDescription = [[NSExpressionDescription alloc] init];
// The name is the key that will be used in the dictionary for the return value.
[expressionDescription setName:#"maxValueY"];
[expressionDescription setExpression:expression];
[expressionDescription setExpressionResultType:NSDecimalAttributeType]; // For example, NSDateAttributeType
// Set the request's properties to fetch just the property represented by the expressions.
[request setPropertiesToFetch:[NSArray arrayWithObject:expressionDescription]];
// Execute the fetch.
NSError *error;
id requestedValue = nil;
NSArray *objects = [context executeFetchRequest:request error:&error];
if (objects == nil) {
// Handle the error.
}
else {
if ([objects count] > 0) {
requestedValue = [[objects objectAtIndex:0] valueForKey:#"maxValueY"];
}
}
[expressionDescription release];
[request release];
return [requestedValue doubleValue];
So far I've been trying to help myself by reading docs, searching through Google and stackoverflow. Everything I seem to find either selects entities (NSPredicates) or selects values using a function. Never have I seen an example or an explanation doing what I'd like to do. I like Core Data, but right now SQL seems way more straightforward to me.

after i add new data in core data collection it can't be saved (multiply validation error occured)

The code bellow add to Core data issues, but after it added, I can't save with error (multiply validation error occured)
MySQLIXC *ixcDatabase = [[MySQLIXC alloc] init];
NSArray *destinationsForSaleList = [ixcDatabase destinationsForSaleList:carrier];
NSFetchRequest *request1 = [[[NSFetchRequest alloc] init] autorelease];
[request1 setEntity:[NSEntityDescription entityForName:#"DestinationsList"
inManagedObjectContext:managedObjectContext]];
for (NSDictionary *destinationsForSale in destinationsForSaleList) {
NSManagedObject *object1 = [NSEntityDescription
insertNewObjectForEntityForName:#"DestinationsList"
inManagedObjectContext:managedObjectContext];
NSLog(#"Moc: %#",managedObjectContext);
[object1 setValue:#"y" forKey:#"weAreSoldIt"];
// changeDate
NSString *chdate = [destinationsForSale objectForKey:#"chdate"];
NSDateFormatter *changeDate = [[[NSDateFormatter alloc] init] autorelease];
[object1 setValue:[changeDate dateFromString:chdate] forKey:#"changeDate"];
NSLog(#"Carrier :%# Destination name:%#",carrier, destinationsForSale);
//Country
[object1 setValue:[destinationsForSale objectForKey:#"country"] forKey:#"country"];
//rate
NSNumberFormatter *rate = [[[NSNumberFormatter alloc]init ]autorelease];
[object1 setValue:[rate numberFromString:[destinationsForSale objectForKey:#"price"]] forKey:#"rate"];
Unfortunately I can't fix a bug by the way which u propose bellow.
Bcs Entity DestinationList must have relations with Entity Carriers by project understanding.
That is how I try to fix it:
[objectDestinationList setValue:objectCarrier forKey:#"carrier"];
I was send to method my carrier object as object, but it doesn't work.
In this case, I don't know how is a way to fix it around. Bcs I see error, but don't see case why error is start.
Do u know a simple code to correct add relationships to Entity? All what I catch around internet is a core data book ,my Marcus Zarra and his very hard to understanding example. His showing a complex solution, I can understand it, but using programming style which not very easy for me at this moment (according my one month experience in cocoa programming ;)
Here is additional information: How I create Carrier instance. I have managedObjectContext, which I receive to class from AppDelegate.
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
[request setEntity:[NSEntityDescription entityForName:#"Carrier"
inManagedObjectContext:managedObjectContext]];
NSManagedObject *carrier = [managedObjectContext executeFetchRequest:request error:&error]
by the same way I prepare NSManagedObject for DestinationsList Entity.
After that I add all values to NSManagedObject for destinationsList, I have to make relationship between Carrer NSManagedObject and destinationsList. In this case I have trouble. Bellow is how I try to update relationship for Carrier entity:
NSSet *newDestSet = [NSSet setWithObjects:objectDestination,nil];
[objectCarrier setValue:newDestSet forKey:#"destinationsList"];
finally I have 2010-11-03 21:22:56.968 snow[20301:a0f] -[NSCFArray initialize]: unrecognized selector sent to instance 0x1c44e40
Bellow is my class interface deescription:
#interface InitUpdateIXC : NSObject {
NSInteger destinationType;
}
-(void) updateCarrierList:(NSManagedObjectContext *)managedObjectContext;
-(void)updateDestinationList:(NSManagedObjectContext *)managedObjectContext
forCarrier:(NSString *)carrierName
forCarrierObject:(NSManagedObject *)objectCarrier
destinationType:(NSInteger)destinationType;
#end
Yep, bellow in answer present correct model, but some different is here.
At first, i don't have separate class for Entity as u present in you model. My current class is just NSManagedObject
In second, relationship "carrier" is non-optional for Entity DestinationsList.
SOLUTION AND ERROR DESCRIPTION:
In case of trouble, what happened with my code:
When i try to add setValue forKey with back relationship from DestinationsList to Carrier, i forget that NSManagmentObject return every time array, not just object.
This is a reason why i receive error about array init problem.
Solution is not sent Carrier object to method, bcs for me was too hard to extract from array correct object without key value. I was using a predicate access to extract object to array and lastObject function to extract correct object from array. After that i set accroding value and everything working fine.
A solution not look like cocoa-style, so better way is refactor it in future, any suggestion wellcome.
Here is appropriate code:
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
[request setEntity:[NSEntityDescription entityForName:#"Carrier"
inManagedObjectContext:managedObjectContext]];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"name =%#",carrierName];
[request setPredicate:predicate];
NSError *error = nil;
NSArray *currentCarriers = [managedObjectContext executeFetchRequest:request error:&error];
[objectDestination setValue:[currentCarriers lastObject] forKey:#"carrier"];
Try adding something like this for you 'save'
NSError *error = nil;
if (![managedObjectContext save:&error])
{
// Handle the error.
NSLog(#"Failed to save to data store: %#", [error localizedDescription]);
NSArray* detailedErrors = [[error userInfo] objectForKey:NSDetailedErrorsKey];
if(detailedErrors != nil && [detailedErrors count] > 0)
{
for(NSError* detailedError in detailedErrors)
{
NSLog(#" DetailedError: %#", [detailedError userInfo]);
}
}
else
{
NSLog(#" %#", [error userInfo]);
}
}
At least, then you can see what the multiple errors are. If you post those, someone may be able to offer more help.
One thought, though, is that there is something buggy about your data model - like non-optional attribute with no value, etc.
If you create NSManagedObject subclassed Carrier and DestinationsList, then in Carrier.h you should have some method declarations like this. (Assuming that Carrier to-many DestinationsList is called 'destinationsLists'.)
#interface Carrier (CoreDataGeneratedAccessors)
- (void)addDestinationsListsObject:(Run *)destinationsList;
- (void)removeDestinationsListsObject:(Run *)destinationsList;
- (void)addDestinationsLists:(NSSet *)destinationsLists;
- (void)removeDestinationsLists:(NSSet *)destinationsLists;
#end
Once these are declared, you should be able to add a DestinationsList to a Carrier with a line like this:
[myCarrier addDestinationsListsObject:myDestinationsList];
Not seeing your full data model, it is difficult to know what is happening and what would help fix it.
Do you have something like this for your model definition?

Resources