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.
精彩评论