开发者

Setting an attribute on an entity and retrieving it with "one-to-many" relations

I have been through the Apple Developer guides and tutorials and I been through 2 iPhone books brushing on the subject of Core Data.

I am used to handling the "value object"/"entity" side of things and then send them of to a web service or the l开发者_JS百科ikes. But on the iPhone I get to handle everything myself… cruel world :)

The Locations, TaggedLocations and PhotoLocations examples from the Apple Developer site do not give me the answers in a way I can "compute". I hope someone here can enlighten me.

I have set up my model using the datamodel GUI. two entities, Person and Dream. Person has a personName string attribute and a one-to-many dreams relationship. Dreams has a description string attribute and a one-to-one person relationship.

I have been setting up a simple tableView app. First view is a list of persons and the second view is a list of their dreams.

This is how I add a person to the modelObjectContext:

Person *newPerson = (Person *)[NSEntityDescription
    insertNewObjectForEntityForName:@"Person" 
    inManagedObjectContext:managedObjectContext];
[newPerson setPersonName:@"Ben Hur"];

OK, I then add a new dream to the context:

Dream *newDream = (Dream *)[NSEntityDescription
    insertNewObjectForEntityForName:@"Dream"
    inManagedObjectContext:managedObjectContext];
[newDream setDescription:@"I had a nightmare"];

I now add the dream to the person like this:

[newPerson addDreamObject:newDream];

Here it gets a bit hazy to me, because xcode generated different methods/accessors for me on the Person Class:

@class Dream;

@interface Person :  NSManagedObject  
{
}

@property (nonatomic, retain) NSString * personName;
@property (nonatomic, retain) NSSet* dream;

@end


@interface Person (CoreDataGeneratedAccessors)
- (void)addDreamObject:(Dream *)value;
- (void)removeDreamObject:(Dream *)value;
- (void)addDream:(NSSet *)value;
- (void)removeDream:(NSSet *)value;

@end

In other situations, where I did not have to handle the actual saving, retrieving, data. I would have build an entity/value object called person and given it an Array to store the dreams. But this is not a possible attribute type in core data, and not the way to do it, I have read (in here in similar threads too).

So how does this boilerplate code work? Am I supposed to use the addDream and send it an NSSet filled with dreams? or can I just trust core data to instantiate this and exclusively use the addDreamObject send the Person entity objects of type Dreams?

I also save the context using the boilerplate code from xcode. Now I wish to update the view with this person, more precisely his name.

In the cellForRowAtIndexPath method I give it this:

NSManagedObject *managedObject = [fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = [[managedObject valueForKey:@"personName"] description];

Again all is well and the name is displayed on the list.

I set up my DreamViewController to take a Person object as a parameter.

Person *selectedObject = [[self fetchedResultsController] objectAtIndexPath:indexPath];
dreamView.selectedPerson = selectedObject;

Then I push the viewController onto the stack and we enter the DreamView. Here I can not seem to get at the at the dreams related to the person I "sent along" with the view.

This is what I'm trying in the DreamViewController's viewDidLoad method (selectedPerson is the accessor I use to pass the Person object):

- (void)viewDidLoad {
[super viewDidLoad];
self.title = @"One Person";

NSManagedObjectContext *context = selectedPerson.managedObjectContext;

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Dream"
                                          inManagedObjectContext:context];
[fetchRequest setEntity:entity];

NSError *error;
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
if (fetchedObjects == nil) {
    // Handle the error.
    NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
    exit(-1);  // Fail
}

NSMutableArray *mutableArray = [fetchedObjects mutableCopy];
self.dreamArray = mutableArray;
NSLog(@"the length of dreamArray: %i",[self.dreamArray count] );

Dream *d = [dreamArray objectAtIndex:0];
NSLog(@"The Dream object says: %@", [d description]);
[mutableArray release];

[fetchRequest release];
}

I really can't seem to get the hang of this and my current experience with Objective C does not allow me to just grab the "best practice" essence from between the lines of Apple's documentation.


You need to correct a mistake you made in your model, to begin with. You can NOT have an attribute called "description" in your dreams entity: this is forbidden because "description" it's the name of a method.

From the Apple documentation (Core Data programming guide):

Note that a property name cannot be the same as any no-parameter method name of NSObject or NSManagedObject, for example, you cannot give a property the name “description” (see NSPropertyDescription).

The difference between addDreamObject: and addDream: is that the former is used to insert a Dream object in the to-many relationship, while the latter is used to insert or replace one-shot the contexts of the to-many relationship.

You should not use

cell.textLabel.text = [[managedObject valueForKey:@"personName"] description];

you should use simply

cell.textLabel.text = [managedObject valueForKey:@"personName"];

Regarding the dreams related to your person, you do not need an additional fetch request. Once you have your person object, you simply access that person's dreams as follows:

for(Dream *dream in person.dreams){
   // process your Dream object
}

Finally, it is not clear why you do not pass explicitly the managed object context to your DreamViewController as an instance variable. This is common practice, also shown in Apple sample codes. Another error is checking for

if (fetchedObjects == nil)

because it is legal to return nil if the query actually found no objects; you must instead check if your NSError object is not nil (you must initialize it to nil BEFORE executing your fetch request):

if(error)

The statement

NSLog(@"The Dream object says: %@", [d description]);

may even crash your application, as explained at the beginning of my answer.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜