views:

323

answers:

2

I'm hoping someone can help me debugging an incredibly frustrating Core Data crash. I initially load data into two entities (representing 'speakers' and 'titles') I then load a third entity for 'sessions' and try to set relationships to 'speakers' and 'titles'. (from session to speaker and title is one-to-one. from speaker/title back to session is one-to-many). I use a integer key to set up each relationship.

Since a given speaker and title can point to multiple sessions, I wrote a function that searches for the appropriate managed object and returns the object. I then set it for the relationship.

This works fine for the speaker relationship but crashes consistently and horribly for the SECOND title. I've rewritten the code a couple of times in different ways and I always end up with the same problem. And the problem exists regardless of what title comes second. So I've gotta be doing something just fundamentally wrong but following along to the Core Data chapters in More iPhone 3 Development nothing jumps out to me. I'm hoping someone might see what I'm missing (and have been for days). (One last note: the crash occurs whether I save the managedObjectContext within the for loop or outside. Always on the second session). My endless thanks and first born child to whoever can help me with this.

Here's the relevent code that saves the session entity:

    for (NSDictionary *session in self.sessions){
        NSManagedObject *newSession = [NSEntityDescription insertNewObjectForEntityForName:[sessionEntity name] inManagedObjectContext:self.managedObjectContext];
        [newSession setValue:[session valueForKey:@"ID"] forKey:@"id"];
        [newSession setValue:[session valueForKey:@"notes"] forKey:@"notes"];
        [newSession setValue:[session valueForKey:@"length"] forKey:@"length"];

        //get the speaker value;
        [newSession setValue:[self setupSpeaker:[session valueForKey:@"speaker"]] forKey:@"speaker"];
        NSLog(@"now doing title");
        //now get the title value;
        [newSession setValue:[self setupTitle:[session valueForKey:@"title"]] forKey:@"title"];
        NSLog(@"I got back this title:%@", [newSession valueForKey:@"title"]);

        }   

        if (![self.managedObjectContext save:&error]) {
            NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
            abort();

}

Here's the code that finds the appropriate speaker and title entity for the relationship (I realize it's pretty redundant)

-(NSManagedObject *) setupSpeaker:(NSNumber *)id {
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Speaker" inManagedObjectContext:self.managedObjectContext];
    [fetchRequest setEntity:entity];

    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"id==%@",id];
    [fetchRequest setPredicate:predicate];

    NSError *error;
    NSArray *items=[self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
    [fetchRequest release];

    if ([items count]>=1){
        return [items objectAtIndex:0];
    }else{
        return 0;
    }
}
-(NSManagedObject *) setupTitle:(NSNumber *)id {
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Title" inManagedObjectContext:self.managedObjectContext];
    [fetchRequest setEntity:entity];
    NSLog(@"now looking for: %@", id);
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"id==%@",id];
    [fetchRequest setPredicate:predicate];

    NSError *error;
    NSArray *items=[self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
    [fetchRequest release];

    if ([items count]>=1){
        NSLog(@"found %@", id);
        return [items objectAtIndex:0];
    }else{
        return 0;
    }
}

And finally here's what the logs says on crash:

2010-02-20 16:48:17.134 iconf[1438:207] now looking for: 1
    2010-02-20 16:48:17.136 iconf[1438:207] found 1
    2010-02-20 16:48:17.156 iconf[1438:207] I got back this title:<NSManagedObject: 0x3b11a10> (entity: Title; id: 0x3d4a3c0 <x-coredata://B76F62BD-AC82-4335-9013-7529C2471F9C/Title/p6> ; data: {
        id = 1;
        session =     (
            0x3d51640 <x-coredata:///Session/t2765697F-14C9-4282-A067-10A2413732B834>
        );
        title = "Bill Gates Speaks";
    })
    2010-02-20 16:48:17.158 iconf[1438:207] now doing title
    2010-02-20 16:48:17.158 iconf[1438:207] now looking for: 2
    2010-02-20 16:48:17.159 iconf[1438:207] found 2
    2010-02-20 16:48:17.161 iconf[1438:207] I got back this title:<NSManagedObject: 0x3b16fd0> (entity: Title; id: 0x3d4d7a0 <x-coredata://B76F62BD-AC82-4335-9013-7529C2471F9C/Title/p12> ; data: {
        id = 2;
        session =     (
            0x3b1b320 <x-coredata:///Session/t2765697F-14C9-4282-A067-10A2413732B835>
        );
        title = "Lecture on Frogs";
    })
    2010-02-20 16:48:17.161 iconf[1438:207] *** -[NSManagedObject compare:]: unrecognized selector sent to instance 0x3b11a10
    2010-02-20 16:48:17.162 iconf[1438:207] Serious application error.  Exception was caught during Core Data change processing: *** -[NSManagedObject compare:]: unrecognized selector sent to instance 0x3b11a10 with userInfo (null)
    2010-02-20 16:48:17.163 iconf[1438:207] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSManagedObject compare:]: unrecognized selector sent to instance 0x3b11a10'
    2010-02-20 16:48:17.163 iconf[1438:207] Stack: (
+1  A: 

It sounds like your data model (should) look like this:

Speaker <-->> Session
Title <-->> Session

Where both Speaker and Title each have a to-many relationship to Session.

Based on this:

I use a integer key to set up each relationship.

and your code, it looks like you are managing the relationships manually. Why are you doing this?! It is complicated and unnecessary. Set up and use real relationships in your Core Data data model.

Also, do not use "id" for an attribute name since it is a keyword in objective-C.

gerry3
A: 

Thanks, below is a copy of the data model. You are right about the basic layout.

http://www.freeimagehosting.net/image.php?debf5866c1.jpg

The reason I'm setting the relationships manually is because this is the first time through the program so I'm doing the initial load of data from three separate plists (one for speaker, one for title, one for session). Is there a better way to do this? I could be fundamentally not understanding core data but it seems as though if I just created a new title entity each time I created the session entity I'd have a one-to-one relationship from title to session rather than the one-to-many relationship that I want. Thus I put in the id variables (which I've now renamed with no change in the error) to act as a key for the first load into core data. After that I'd of course use core data to manage all that. Is there a better way to do this?

Martin
First, do you have two SO users? Why? Next, you should never post additional content for a question as an answer. You can edit your original question or create a new question if appropriate.
gerry3
You can do the initial load from plists OR you could do the initial load during development, then save that database file and add it to your project. Then, in your released app, you would copy the database file out of your read-only app bundle into the Documents directory. See http://stackoverflow.com/questions/2265333/how-to-populate-core-data-with-initial-data/2267000#2267000
gerry3
You do not understand Core Data relationships. The first thing you should do is create subclasses for your entities. Then you can access the attributes and relationships through their names as properties of your objects. The to-many relationships are actually sets, so you can add a single object or a set of objects with methods that are provided for you when you create the subclass in Xcode.
gerry3
Yeah, my sincere apologies about my misuse of stack overflow. I accidentally posted my first post anonymously and so when I went to post a comment with additonal details I couldn't post a comment as myself because of my low rating. Anyway, I'll do better in the future.I just figure out the error. In code above what I had posted I was using a fetchedResultsController on "title" to check to see if there were any results. I got rid of that and I was good to go.Thanks again for your patience with a noob.
Martin