NSManagedObject not being persisted - no matter how hard I try :( - iphone

I have a fairly simple iPhone app which utilizes CoreData for object persistence.
The object has, amongst other attributes, an NSNumber attribute, defined in the datamodel along with all the other attributes. I set this during the application run cycle to 1 if the user clicks on a particular button. I then call the store function which definitely does get called, and is the same function as persisted everything else, and this seems to work temporarily, in that if I check the value of the attribute on my NSManagedObject it has the correct value, if I retrieve the object from the data store and check it it still has the right value. However if I restart the app, it has not persisted, and so it reverts to the default. I'm getting quite frustrated and have tried various methods of forcing the ManagedObjectContext to persist.
Relevant code:
Persistence code...
- (Area*) storeAreaFavourite:(Area*)a
{
a = [self storeArea:a];
NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
//[context refreshObject:a mergeChanges:YES];
[context processPendingChanges];
NSLog(#"Stored area with favourite: %#",([a favourite] != nil ? [a favourite] : [NSNumber numberWithInt: 0]));
return a;
}
- (Area*) storeArea:(Area*)a
{
NSError *error = nil;
// Create a new instance of the entity managed by the fetched results controller.
NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
NSFetchRequest* request = [[NSFetchRequest alloc] init];
NSEntityDescription* entity = [NSEntityDescription entityForName:#"Area" inManagedObjectContext:context];
NSPredicate* predicate = [NSPredicate predicateWithFormat:#"areaId=%#", [a areaId]];
[request setEntity:entity];
[request setPredicate:predicate];
NSArray* matchedAreas = [context executeFetchRequest:request error:&error];
if (error != nil)
{
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
//NSLog(#"Matched %d Areas", [matchedAreas count]);
Area* newArea = [matchedAreas count] > 0 ? [matchedAreas objectAtIndex:0] : nil;
if (newArea == nil)
{
newArea = [NSEntityDescription insertNewObjectForEntityForName:#"Area" inManagedObjectContext:context];
}
else {
//NSLog(#"Area: %# -> %# ParentArea: %# -> %#", [newArea valueForKey:#"areaId"], [a areaId], [(Area*)[newArea valueForKey:#"parentArea"] areaId], [(Area*)[a parentArea] areaId]);
}
// If appropriate, configure the new managed object.
[newArea setValue:[a areaId] forKey:#"areaId"];
[newArea setValue:[a areaName] forKey:#"areaName"];
[newArea setValue:[a parentArea] forKey:#"parentArea"];
[newArea setValue:[a height] forKey:#"height"];
[newArea setValue:[a width] forKey:#"width"];
[newArea setValue:[a xPos] forKey:#"xPos"];
[newArea setValue:[a yPos] forKey:#"yPos"];
[newArea setValue:[a childAreas] forKey:#"childAreas"];
[newArea setValue:[a imageName] forKey:#"imageName"];
[newArea setValue:[a areaText] forKey:#"areaText"];
[newArea setValue:([a favourite] != nil ? [a favourite] : [NSNumber numberWithInt: 0]) forKey:#"favourite"];
if ([a favourite] != nil && [[NSNumber numberWithInt:1] isEqualToNumber:[a favourite]])
{
NSLog(#"Storing area with areaId: %#",[a areaId]);
NSLog(#"Storing area with areaName: %#",[a areaName]);
NSLog(#"Storing area with parentArea: %#",[a parentArea]);
NSLog(#"Storing area with height: %#",[a height]);
NSLog(#"Storing area with width: %#",[a width]);
NSLog(#"Storing area with xPos: %#",[a xPos]);
NSLog(#"Storing area with yPos: %#",[a yPos]);
NSLog(#"Storing area with childAreas: %#",[a childAreas]);
NSLog(#"Storing area with imageName: %#",[a imageName]);
NSLog(#"Storing area with areaText: %#",[a areaText]);
NSLog(#"Storing area with favourite: %#",([a favourite] != nil ? [a favourite] : [NSNumber numberWithInt: 0]));
}
// Save the context.
if (![context save:&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. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button.
*/
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
//abort();
}
matchedAreas = [context executeFetchRequest:request error:&error];
if (error != nil)
{
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
//NSLog(#"Matched %d Areas", [matchedAreas count]);
newArea = [matchedAreas count] > 0 ? [matchedAreas objectAtIndex:0] : nil;
if (newArea != nil)
{
NSLog(#"StoredFav:%#:%#",[newArea areaId],[newArea favourite]);
}
return newArea;
}
Area.m
//
// Area.m
// MappApp
//
// Created by Matthew Fellows on 27/07/2011.
//
#import "Area.h"
#implementation Area
#synthesize height;
#synthesize areaId;
#synthesize xPos;
#synthesize areaName;
#synthesize width;
#synthesize areaText;
#synthesize imageName;
#synthesize yPos;
#synthesize childAreas;
#synthesize parentArea;
#synthesize areaImages;
#synthesize favourite;
- (void)addChildAreasObject:(NSManagedObject *)value{
if (childAreas == nil)
{
childAreas = [[NSMutableSet alloc] init];
}
[childAreas addObject:value];
}
- (void)addAreaImagesObject:(NSManagedObject *)value{
if (areaImages == nil)
{
areaImages = [[NSMutableSet alloc] init];
}
[areaImages addObject:value];
}
#end
Area.h
//
// Area.h
// MappApp
//
// Created by Matthew Fellows on 27/07/2011.
//
#import <CoreData/CoreData.h>
#interface Area : NSManagedObject
{
NSMutableSet* areaImages;
NSMutableSet* childAreas;
}
#property (nonatomic, retain) NSNumber * height;
#property (nonatomic, retain) NSNumber * areaId;
#property (nonatomic, retain) NSNumber * xPos;
#property (nonatomic, retain) NSString * areaName;
#property (nonatomic, retain) NSNumber * width;
#property (nonatomic, retain) NSString * areaText;
#property (nonatomic, retain) NSString * imageName;
#property (nonatomic, retain) NSNumber * yPos;
#property (nonatomic, retain) NSMutableSet* childAreas;
#property (nonatomic, retain) NSManagedObject * parentArea;
#property (nonatomic, retain) NSMutableSet* areaImages;
#property (nonatomic, retain) NSNumber* favourite;
#end
#interface Area (CoreDataGeneratedAccessors)
- (void)addChildAreasObject:(NSManagedObject *)value;
- (void)removeChildAreasObject:(NSManagedObject *)value;
- (void)addChildAreas:(NSSet *)value;
- (void)removeChildAreas:(NSSet *)value;
- (void)addAreaImagesObject:(NSManagedObject *)value;
- (void)removeAreaImagesObject:(NSManagedObject *)value;
- (void)addAreaImages:(NSSet *)value;
- (void)removeAreaImages:(NSSet *)value;
#end

You have #synthesized your accessors for what are presumably managed object properties. This means they won't be passing through the correct core data accessors and will not be updating your model properly. #dynamic should be used to tell the compiler that the proper accessors will be available at run time (the core data framework will be providing them).
I think with what you have now you have basically made all of your attributes transient.

You should make all your properties #dynamic.
The #synthesize will create a getter and setter for each property, but will do nothing for the CoreData storage system.
That is, changing the values will not cause CoreData to know they have changed, and thus to store them.

Related

Archiving and retrieving array from a class in Objective-c [closed]

It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 9 years ago.
Hi i am trying to save data in a file with the help of a class that is saved to disk. It is saving the data but not retrieving correct data. Plz help me
Here is my ViewController.h
#interface MIGViewController : UIViewController
- (IBAction)hideKeyboard:(id)sender;
#property (strong, nonatomic) NSMutableArray * myArray;
#property (strong, nonatomic) NSString * pathToFile;
#property (weak, nonatomic) IBOutlet UITextField *nameField;
#property (weak, nonatomic) IBOutlet UITextField *idField;
#property (weak, nonatomic) IBOutlet UITextField *addressField;
#property (weak, nonatomic) IBOutlet UITextField *phoneField;
- (IBAction)addButtonTapped:(id)sender;
- (IBAction)saveButtonTapped:(id)sender;
#end
Here is view Controller.m
#import "MIGViewController.h"
#import "MIGStudent.h"
#interface MIGViewController ()
#end
#implementation MIGViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSArray * paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString * documentsPath = [paths objectAtIndex:0];
self.pathToFile = [documentsPath stringByAppendingPathComponent:#"students.sukh"];
NSFileManager * manager = [NSFileManager defaultManager];
if ([manager fileExistsAtPath:self.pathToFile])
{
//File exists
self.myArray = [NSKeyedUnarchiver unarchiveObjectWithFile:self.pathToFile];
UIAlertView * alert = [[UIAlertView alloc]initWithTitle:#"Students array" message:[self.myArray description]delegate:nil cancelButtonTitle:#"Dismiss" otherButtonTitles:nil, nil];
[alert show];
}
else
{
//File doesn't exist
self.myArray = [NSMutableArray array];
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)addButtonTapped:(id)sender {
NSString * name =self.nameField.text;
NSString * address = self.addressField.text;
int studentID = [self.idField.text intValue];
int phoneNumber = [self.phoneField.text intValue];
MIGStudent * student = [[MIGStudent alloc] initWithName:name address:address studentID:studentID phoneNumber:phoneNumber];
[self.myArray addObject:student];
self.nameField.text = #"";
self.addressField.text = #"";
self.idField.text = #"";
self.phoneField.text = #"";
}
- (IBAction)saveButtonTapped:(id)sender {
[NSKeyedArchiver archiveRootObject:self.myArray toFile:self.pathToFile];
}
- (IBAction)hideKeyboard:(id)sender {
[self resignFirstResponder];
}
#end
Here is my class Student.h
#interface MIGStudent : NSObject <NSCoding>
#property (strong, nonatomic) NSString * name;
#property (strong, nonatomic) NSString * address;
#property (nonatomic) int studentID;
#property (nonatomic) int phoneNumber;
-(id)initWithName:(NSString *) name address:(NSString *)address studentID: (int)studentID phoneNumber:(int) phoneNumber;
#end
And here is Student.m
#import "MIGStudent.h"
#implementation MIGStudent
-(id) initWithName:(NSString *)name address:(NSString *)address studentID:(int)studentID phoneNumber:(int)phoneNumber
{
if (self=[super init])
{
self.name = name;
self.address = address;
self.phoneNumber = phoneNumber;
self.studentID = studentID;
}
return self;
}
-(void) encodeWithCoder:(NSCoder *)aCoder
{
//Used when saving to disk
[aCoder encodeObject:self.name forKey:#"name"];
[aCoder encodeObject:self.address forKey:#"address"];
[aCoder encodeInt:self.phoneNumber forKey:#"phoneNumber"];
[aCoder encodeInt:self.studentID forKey:#"studentID"];
}
-(id) initWithCoder:(NSCoder *)aDecoder
{
//Used when reading from disk
self.name = [aDecoder decodeObjectForKey:#"name"];
self.address = [aDecoder decodeObjectForKey:#"address"];
self.phoneNumber = [aDecoder decodeIntForKey:#"phoneNumber"];
self.studentID = [aDecoder decodeIntForKey:#"studentID"];
return self;
}
#end
And here is what i get output every time
I think everythings is working properly but there is some problem while retrieving the data
Thanks in advance
That's not a problem with your implementation of NSCoding, that's actually not even a problem at all! That's the way an NSArray prints itself literally (the result of the -description method). The fact that it does this indicates that the archiving/de-archiving process went smoothly. The array is the set of parenthesis (), and the objects within are in the format <Class : memory address>. If you had an array of invalid objects, then your array would most likely refuse to print, or crash when the students objects were added to it.
You can take advantage of this in your MIGStudent class and override -description to print a friendlier format. For example:
-(NSString*)description {
return [NSString stringWithFormat:#"<%# : %p Student named: %# - who lives at: %# With the ID number: %d and the phone number: %d>", NSStringFromClass(self.class), self, self.name, self.address, self.studentID, self.phoneNumber];
}
As a sidenote, initializers always call through to super. Your initWithCoder: method will always return nil.
Your initWithCoder: method is incorrect it will fail as currently it will return nil or some garbage value, it should be:
-(id) initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self)
{
//Used when reading from disk
self.name = [aDecoder decodeObjectForKey:#"name"];
self.address = [aDecoder decodeObjectForKey:#"address"];
self.phoneNumber = [aDecoder decodeIntForKey:#"phoneNumber"];
self.studentID = [aDecoder decodeIntForKey:#"studentID"];
}
return self;
}
Secondly you can either override description or debugDescription method of your class to return custom information about that class.

Unacceptable type of value for attribute:

I have a problem with the project I found in the book. The project will pop different shapes when the screen is tapped. My problem is that it creates an exception, and I believe the problem is a line inside createShapeAt:
In the NSInvalidArgumentException, property is found in updateAllShapes: desired type of NSString is what it needs but the output is of type UIDeviceRGBColor. Am I right?
It has an exception:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Unacceptable type of value for attribute: property = "color"; desired type = NSString; given type = UIDeviceRGBColor; value = UIDeviceRGBColorSpace 0.0980392 0.0705882 0.152941 1.'
Code:
- (void)createShapeAt:(CGPoint)point
{
Shape *shape = nil;
int type = arc4random() % 2;
if (type == 0) {
shape = [Circle randomInstance:point inContext:self.managedObjectContext];
}
else {
shape = [Polygon randomInstance:point inContext:self.managedObjectContext];
}
NSLog(#"Test: %#", [[self makeRandomColor] description]);
shape.color = [self makeRandomColor]; //This is where the Exception kicks in.
}
- (UIColor *)makeRandomColor
{
float red = (arc4random() % 256) / 255.0;
float green = (arc4random() % 256) / 255.0;
float blue = (arc4random() % 256) / 255.0;
return [UIColor colorWithRed:red green:green blue:blue alpha:1.0];
}
- (void)updateAllShapes
{
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc]init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Shape" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
NSArray *shapes = [managedObjectContext executeFetchRequest:fetchRequest error:nil];
for (NSManagedObject *shape in shapes) {
[shape setValue:[self makeRandomColor] forKey:#"color"];
}
NSError *error = nil;
if (![self.managedObjectContext save:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
}
Shape.h
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#class Canvas;
#interface Shape : NSManagedObject
#property (nonatomic, retain) UIColor *color;
#property (nonatomic, retain) NSSet *canvases;
#end
#interface Shape (CoreDataGeneratedAccessors)
- (void)addCanvasesObject:(Canvas *)value;
- (void)removeCanvasesObject:(Canvas *)value;
- (void)addCanvases:(NSSet *)values;
- (void)removeCanvases:(NSSet *)values;
#end
Shape.m
#import "Shape.h"
#import "Canvas.h"
#implementation Shape
#dynamic color;
#dynamic canvases;
#end
Console:
Test: UIDeviceRGBColorSpace 0.0431373 0.764706 0.223529 1
How do I change this line of code: shape.color = [self makeRandomColor]; to become an NSString?
Edit: I have added Shape.h and Shape.m
declare one property like below ,be sure it is retain
#property (nonatomic, retain) UIColor *color;
& then assign
shape.color=[self makeRandomColor];

Overwrite a value saved in NSUserDefaults

I have this method to save a mutable array named myWallet that contains instances of the Class Card.
- (void)saveMyWallet
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:[NSKeyedArchiver archivedDataWithRootObject:self.myWallet] forKey:#"myWalletArray"];
[defaults synchronize];
}
The Card Class that I have has three instance variables: name, pin, and points. So far, saving new instances of the Card in UserDefaults is ok. I would just like to know some suggestions on how can I overwrite the value of points because as I proceed in the computation of points, I want to update it.
Here is my Card Class
Card.h
#import <Foundation/Foundation.h>
#interface Card : NSObject <NSCoding>
#property (nonatomic, strong) NSString *name;
#property (nonatomic, strong) NSString *pin;
#property (nonatomic, strong) NSNumber *points;
#property (nonatomic, strong) NSMutableArray *pointsToDeduct;
- (double) subtractPoints: (double) requiredPoints;
- (void) encodeWithCoder:(NSCoder *)coder;
- (id) initWithCoder: (NSCoder *)coder;
#end
Card.m
#import "Card.h"
#implementation Card
#synthesize name = _name;
#synthesize pin = _pin;
#synthesize points = _points;
#synthesize pointsToDeduct = _pointsToDeduct;
- (id)initWithCoder:(NSCoder *)coder
{
self = [[Card alloc] init];
if(self != nil) {
self.name = [coder decodeObjectForKey:#"name"];
self.pin = [coder decodeObjectForKey:#"pin"];
self.points = [coder decodeObjectForKey:#"points"];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)coder
{
[coder encodeObject:self.name forKey:#"name"];
[coder encodeObject:self.pin forKey:#"pin"];
[coder encodeObject:self.points forKey:#"points"];
}
- (double) subtractPoints:(double) requiredPoints
{
double latestPoints;
latestPoints = ([self.points doubleValue] - requiredPoints);
return latestPoints;
}
#end
And lastly, here is the delegate method by which the new value of the points (named resultingPoints) should come from.
- (void)perksDetailsViewController:(PerksDetailsViewController *)sender didPassRequiredPoints:(NSNumber *)requiredPoints withCard:(Card *)selectedCard
{
double perksPoints = [requiredPoints doubleValue];
self.resultingPoints = [NSNumber numberWithDouble:[selectedCard subtractPoints:perksPoints] ];
NSLog(#"points remaining %#", self.resultingPoints);
}
Bombard me with suggestions :) Thanks in advance!
From what I see, you actually save your object as NSData, so the logical approach is to get it back from the user defaults, unarchive it, update the properties, archive it and save it back to the user defaults.
Retrive the data from NSUserDefaults into runtime,Delete previous object for key and write back updated value.

App hanging/crashing after adding NSSet of entities with relationships

I have to main issues that I believe are related as they both occur on the same line of code.
Data Model
NB: I have simplified the code and model as best I can.
I have 3 entities in my Core data model.
Merchant (can have many Branches, can have many Sectors)
Sector (can have many Merchants)
Branch (can have one Merchant)
Data is downloaded (in JSON) to the app. Each Merchant is iterated over sectors are extracted, if the sector exists it is fetched and added to a NSMutableArray.
...
//Iterating through Merchants
...
for(NSDictionary *sector in sectors) {
NSLog(#"\tfetch sectors ID %#", [sector objectForKey:#"sector_id"]);
NSPredicate *sectorPredicate = [NSPredicate predicateWithFormat:#"%K == %d", #"sectorID", [[sector objectForKey:#"sector_id"] integerValue]];
[sectorRequest setPredicate:sectorPredicate];
NSArray *existingSector = [self.managedObjectContext executeFetchRequest:sectorRequest error:&error];
if(!error && [existingSector count] == 1) {
NSLog(#"\tfound sector");
[merchantSectors addObject:[existingSector objectAtIndex:0]];
}
else {
NSLog(#"\tcreate a new sector");
//Create a new sector
Sector *newSector = [[Sector alloc] initWithEntity:sectorEntity insertIntoManagedObjectContext:self.managedObjectContext];
newSector.sectorID = [NSNumber numberWithInteger:[[sector objectForKey:#"sector_id"] integerValue]];
newSector.name = [sector objectForKey:#"name"];
[merchantSectors addObject:newSector];
[newSector release]; newSector = nil;
}
}
[sectorRequest release]; sectorRequest = nil;
NSLog(#"\tadd sectors to merchant");
[currentMerchant addSector:merchantSectors]; //<---- crash and hang
The App will either hang at:
[currentMerchant addSector:merchantSectors];
or sometimes throw an exception:
*** Terminating app due to uncaught exception \
'NSInternalInconsistencyException', reason: \
'-[__NSCFSet addObject:]: mutating method sent to immutable object'
The Branch parsing code is almost identical but never has these issues or the app will hang or crash before it becomes an issue (??).
If the App is deleted and reinstalled the code will work fine, is it possible that existing identical relationships are causing this problem?
Edit: The parsing of the JSON is called using an NSInvocationOperation, so when it hangs the interface stays responsive. The crash version kills the app.
Edit 2: Merchant.h and Merchant.m
Merchant.h
#import <CoreData/CoreData.h>
#class Branch;
#class Sector;
#interface Merchant : NSManagedObject
{
}
#property (nonatomic, retain) NSString * street;
#property (nonatomic, retain) NSString * locality;
#property (nonatomic, retain) NSString * city;
#property (nonatomic, retain) NSNumber * merchantID;
#property (nonatomic, retain) NSString * postcode;
#property (nonatomic, retain) NSString * property;
#property (nonatomic, retain) NSString * organisation;
#property (nonatomic, retain) NSDate * expires;
#property (nonatomic, retain) NSSet * Branch;
#property (nonatomic, retain) NSSet* Sector;
#end
#interface Merchant (CoreDataGeneratedAccessors)
- (void)addBranchObject:(Branch *)value;
- (void)removeBranchObject:(Branch *)value;
- (void)addBranch:(NSSet *)value;
- (void)removeBranch:(NSSet *)value;
- (void)addSectorObject:(Sector *)value;
- (void)removeSectorObject:(Sector *)value;
- (void)addSector:(NSSet *)value;
- (void)removeSector:(NSSet *)value;
#end
Merchant.m
#import "Merchant.h"
#import "Branch.h"
#implementation Merchant
#dynamic street;
#dynamic locality;
#dynamic city;
#dynamic merchantID;
#dynamic postcode;
#dynamic property;
#dynamic organisation;
#dynamic expires;
#dynamic Branch;
#dynamic Sector;
#end
Try to add Sectors to Merchant one by one using CoreData add<Key>Object: and remove<Key>Object: auto-generated methods (as described in Custom To-Many Relationship Accessor Methods)
for(NSDictionary *sector in sectors) {
NSPredicate *sectorPredicate = [NSPredicate predicateWithFormat:#"%K == %d", #"sectorID", [[sector objectForKey:#"sector_id"] integerValue]];
[sectorRequest setPredicate:sectorPredicate];
NSArray *existingSector = [self.managedObjectContext executeFetchRequest:sectorRequest error:&error];
if(!error && [existingSector count] == 1)
{
[currentMerchant addSectorObject:[existingSector lastObject]];
}
else
{
Sector *newSector = [[Sector alloc] initWithEntity:sectorEntity insertIntoManagedObjectContext:self.managedObjectContext];
newSector.sectorID = [NSNumber numberWithInteger:[[sector objectForKey:#"sector_id"] integerValue]];
newSector.name = [sector objectForKey:#"name"];
[currentMerchant addSectorObject:newSector];
[newSector release];
}
}
Or you can retrieve mutable proxy object contains currentMerchants's sectors via mutableSetValueForKey: and add sectors to it:
NSMutableSet *merchantSectors = [currentMerchant mutableSetValueForKey:#"sector"];
for(NSDictionary *sector in sectors) {
NSPredicate *sectorPredicate = [NSPredicate predicateWithFormat:#"%K == %d", #"sectorID", [[sector objectForKey:#"sector_id"] integerValue]];
[sectorRequest setPredicate:sectorPredicate];
NSArray *existingSector = [self.managedObjectContext executeFetchRequest:sectorRequest error:&error];
if(!error && [existingSector count] == 1)
{
[merchantSectors addObject:[existingSector lastObject]];
}
else
{
Sector *newSector = [[Sector alloc] initWithEntity:sectorEntity insertIntoManagedObjectContext:self.managedObjectContext];
newSector.sectorID = [NSNumber numberWithInteger:[[sector objectForKey:#"sector_id"] integerValue]];
newSector.name = [sector objectForKey:#"name"];
[merchantSectors addObject:newSector];
[newSector release];
}
}
Anyway, for convenience it's better to use lowercase sectors name for Mecrhant entity for to-many relationship with Sector entity: lowercase not to be ambiguous with Sector class name, and with s at and to be sure, that getter methods for this property return multiple objects.

Why is my app crashing when I add a new managed object to a relationship set?

I've this simple Core Data Model:
Question, Answer
Every every question has 4 answers.
The code is the following:
Question.m
#interface Question : NSManagedObject
{
}
#property (nonatomic, retain) NSString * questionText;
#property (nonatomic, retain) NSSet* answers;
#property (nonatomic, retain) Package * package;
#end
#interface Question (CoreDataGeneratedAccessors)
- (void)addAnswersObject:(NSManagedObject *)value;
- (void)removeAnswersObject:(NSManagedObject *)value;
- (void)addAnswers:(NSSet *)value;
- (void)removeAnswers:(NSSet *)value;
#end
Answer.m
#class Question;
#interface Answer : NSManagedObject
{
}
#property (nonatomic, retain) NSString * answerText;
#property (nonatomic, retain) NSNumber * correct;
#property (nonatomic, retain) Question * question;
#end
The problem is when i try to add an answer to a question with addAnswersObject.
This is the part of the code that crash the app:
for (CXMLElement *theElement in theNodes)
{
Question *qst = [NSEntityDescription insertNewObjectForEntityForName:#"Question" inManagedObjectContext:moc];
// Create a counter variable as type "int"
int counter;
// Loop through the children of the current node
for(counter = 0; counter < [theElement childCount]; counter++) {
if([[[theElement childAtIndex:counter] name] isEqualToString: #"question"])
[qst setQuestionText:[[theElement childAtIndex:counter] stringValue]];
if([[[theElement childAtIndex:counter] name] isEqualToString: #"answer"]) {
Answer *answer = [NSEntityDescription insertNewObjectForEntityForName:#"Answer" inManagedObjectContext:moc];
[answer setAnswerText:[[theElement childAtIndex:counter] stringValue]];
CXMLElement *answerElement = (CXMLElement *)[theElement childAtIndex:counter];
if([[[answerElement attributeForName:#"correct"] stringValue] isEqualToString:#"YES"]) {
[answer setCorrect:[NSNumber numberWithBool:YES]];
} else {
[answer setCorrect:[NSNumber numberWithBool:NO]];
}
[qst addAnswersObject:answer]; //The app crash here
}
}
This is the log from console:
2010-05-24 20:02:38.475 Fgq[5670:40b]
*** -[NSUserDefaults objectForKey:]: message sent to deallocated instance
0x3c179a0 Program received signal:
“EXC_BAD_ACCESS”.
I re-exported many times all objects from the Object Data Model without success, I've checked all relationships and it seems that everything is ok.
What kind of problem could be?
What does the Console report if you add NSLog(#"qst: %#:, qst); immediately after the qst instance's -insertNewObjectForEntityName:inManagedObjectContext: call?
What does the Console report if you add NSLog(#"answer: %#:, answer); immediately after the answer instance's -insertNewObjectForEntityName:inManagedObjectContext: call?
Here what is reported with NSLog(#"qst: %#", qst);
2010-05-24 23:37:33.948
FGQ[452:207] qst:
(entity:
Question; id: 0x3c19ab0
; data: {
answers = (
);
package = nil;
questionText = nil; })