Question

I have a one-to-many relationship in core data, in which Recipe has a one-to-many relationship with Step.

When I try to fetch the saved recipes I get this crash:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Step initWithCoder:]: unrecognized selector sent to instance 0x9b30a20'

This is the code that fetches the recipes:

//Create the fetch request
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc]init];

//Here is the entity whose contents we want to read
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Recipe" inManagedObjectContext:self.managedObjectContext];

//Tell the request that we want to read the contents of the Recipe entity
[fetchRequest setEntity:entity];

NSError *requestError = nil;
//Execute the fetch request on the context
self.recipes = [self.managedObjectContext executeFetchRequest:fetchRequest error:&requestError];

It crashes only when I'm fetching the recipes, not when I'm creating and saving.

This is Step.h:

@class Recipe;

@interface Step : NSManagedObject

@property (nonatomic, retain) id photo;
@property (nonatomic, retain) NSString * instruction;
@property (nonatomic, retain) id timer;
@property (nonatomic, retain) Recipe *recipe;

@end

Step.m:

@implementation Step

@dynamic photo;
@dynamic instruction;
@dynamic timer;
@dynamic recipe;

Recipe.h:

@class Step;

@interface Recipe : NSManagedObject

@property (nonatomic, retain) id image;
@property (nonatomic, retain) id ingredients;
@property (nonatomic, retain) NSString * title;
@property (nonatomic, retain) NSOrderedSet *steps;

- (void) addIngredient: (NSString *) ingredient;
- (void) addIngredients:(NSArray *)ingredients;
- (BOOL) saveRecipe;

@end

@interface Recipe (CoreDataGeneratedAccessors)

- (void)insertObject:(Step *)value inStepsAtIndex:(NSUInteger)idx;
- (void)removeObjectFromStepsAtIndex:(NSUInteger)idx;
- (void)insertSteps:(NSArray *)value atIndexes:(NSIndexSet *)indexes;
- (void)removeStepsAtIndexes:(NSIndexSet *)indexes;
- (void)replaceObjectInStepsAtIndex:(NSUInteger)idx withObject:(Step *)value;
- (void)replaceStepsAtIndexes:(NSIndexSet *)indexes withSteps:(NSArray *)values;
- (void)addStepsObject:(Step *)value;
- (void)removeStepsObject:(Step *)value;
- (void)addSteps:(NSOrderedSet *)values;
- (void)removeSteps:(NSOrderedSet *)values;

@end

Recipe.m:

@implementation Recipe

@dynamic image;
@dynamic ingredients;
@dynamic title;
@dynamic steps;

- (void) addSteps:(NSOrderedSet *)values
{
    NSMutableOrderedSet *tmpOrderedSet = [NSMutableOrderedSet orderedSetWithOrderedSet:self.steps];
    NSMutableIndexSet *indexes = [NSMutableIndexSet indexSet];
    NSUInteger valuesCount = [values count];
    NSUInteger objectsCount = [tmpOrderedSet count];
    for (NSUInteger i = 0; i < valuesCount; ++i) {
        [indexes addIndex:(objectsCount + i)];
    }
    if (valuesCount > 0) {
        [self willChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes forKey:@"Step"];
        [tmpOrderedSet addObjectsFromArray:[values array]];
        [self setPrimitiveValue:tmpOrderedSet forKey:@"Step"];
        [self didChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes forKey:@"Step"];
    }
}

Do I need to implement initWithCoder: in Step? If yes, what do I need to do in that method? Why and when is it called?

Edit: When I don't set the image attribute of Recipe, there's no crash. I don't see what it's got to do with Step. I set the Recipe's image like this:

UIImage *squareImage = [self imageCrop:theImage];
recipe.image = squareImage;

This works and saves the recipe. Only when I fetch, the app crashes. It looks like image is being mixed up with Step somewhere. Also, I tried implementing initWithCoder in Step, and then got another crash when I was trying to do something with the image:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSOrderedSetM size]: unrecognized selector sent to instance 0x9891980'

Can somebody help?

Was it helpful?

Solution 2

The problem was a typo in one of the methods and the crash was occurring because of a memory corruption. The crash log revealed nothing related to the typo!

In the addSteps: method in Recipe.m, I was using the class name Step instead of the property name steps.

It should have been like this:

[self willChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes forKey:@"steps"];
[tmpOrderedSet addObjectsFromArray:[values array]];
[self setPrimitiveValue:tmpOrderedSet forKey:@"steps"];
[self didChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes forKey:@"steps"];

instead of:

[self willChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes forKey:@"Step"];
[tmpOrderedSet addObjectsFromArray:[values array]];
[self setPrimitiveValue:tmpOrderedSet forKey:@"Step"];
[self didChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes forKey:@"Step"];

OTHER TIPS

It is called when you archive custom objects. This link can help you understand it

In the above code you have custom Object type "Recipe" in "Step". This answer will help you

Your Recipe class should be as show below for archiving, which is to communicate using key value pairs with data types.

Those method are part of archiving process and un-archiving process.

-(id)initWithCoder:(NSCoder *)aDecoder
{
//read
if(self = [super init]){
    property1=[aDecoder decodeObjectForKey:@"property1"];
}
return self;
}

-(void)encodeWithCoder:(NSCoder *)encoder
{
//save
[encoder encodeObject:property1 forKey:@"property1"];
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top