2009-10-17 3 views
1

J'ai suivi les guides et les didacticiels Apple Developer et j'ai étudié deux livres pour iPhone sur le sujet des données de base.Définition d'un attribut sur une entité et récupération de celle-ci avec des relations "un-à-plusieurs"

Je suis habitué à manipuler le côté "objet de valeur"/"entité" des choses et à les envoyer ensuite à un service web ou à des goûts similaires. Mais sur l'iPhone je reçois tout gérer moi-même ... monde cruel :)

Les Locations, TaggedLocations et PhotoLocations exemples à partir du site Apple Developer ne me donne pas les réponses d'une manière que je peux « calculer ». J'espère que quelqu'un ici peut m'éclairer.

J'ai configuré mon modèle en utilisant l'interface graphique du modèle de données. deux entités, Person et Dream. Person a un attribut de chaîne personName et une relation un-à-plusieurs dreams. Dreams a un attribut de chaîne description et une relation un-à-un person.

J'ai mis en place une application simple tableView. Première vue est une liste de personnes et la deuxième vue est une liste de leurs rêves.

Voici comment ajouter une personne à l'modelObjectContext:

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

OK, je puis ajouter un nouveau rêve dans le contexte:

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

ajouter maintenant le rêve à la personne comme ceci:

[newPerson addDreamObject:newDream]; 

Ici, il devient un peu brumeux pour moi, car xcode a généré différentes méthodes/accesseurs pour moi sur la Person Classe:

@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 

Dans d'autres situations, où je ne dois gérer l'économie réelle, la récupération, les données. J'aurais construit un objet entité/valeur appelé person et lui ai donné un Array pour stocker les rêves. Mais ce n'est pas un type d'attribut possible dans les données de base, et pas la façon de le faire, j'ai lu (ici aussi dans des threads similaires).

Alors, comment ce code passe-partout fonctionne-t-il? Suis-je censé utiliser le addDream et lui envoyer un NSSet rempli de rêves? ou puis-je juste faire confiance aux données de base pour instancier cela et utiliser exclusivement le addDreamObject envoyer les objets entité Person de type Dreams? Je sauvegarde aussi le contexte en utilisant le code de xcode. Maintenant, je souhaite mettre à jour la vue avec cette personne, plus précisément son nom.

Dans la méthode cellForRowAtIndexPath Je lui donne ceci:

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

Encore une fois tout va bien et le nom est affiché sur la liste.

J'ai configuré mon DreamViewController pour prendre en paramètre un objet Person.

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

Puis je pousse le viewController sur la pile et nous entrons dans le DreamView. Ici, je n'arrive pas à atteindre les rêves liés à la personne que j'ai "envoyé" avec la vue.

C'est ce que je suis en train dans la méthode de viewDidLoadDreamViewController (selectedPerson est l'accesseur que j'utilise pour passer l'objet Person):

- (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]; 
} 

Je ne peux pas sembler obtenir le coup de Ceci et mon expérience actuelle avec Objective C ne me permettent pas de saisir l'essence des «meilleures pratiques» entre les lignes de la documentation d'Apple.

Répondre

3

Vous devez d'abord corriger une erreur que vous avez commise dans votre modèle. Vous ne pouvez PAS avoir un attribut appelé "description" dans votre entité de rêve: ceci est interdit car "description" c'est le nom d'une méthode.

De la documentation Apple (guide de programmation de base de données):

Notez que le nom de propriété ne peut pas être le même que tout, par exemple, de NSObject ou NSManagedObject, nom de la méthode sans paramètre vous ne pouvez pas donner une propriété au nom "Description" (voir NSPropertyDescription). La différence entre addDreamObject: et addDream: est que le premier est utilisé pour insérer un objet Dream dans la relation to-many, alors que le dernier est utilisé pour insérer ou remplacer un-shot les contextes de la relation to-many .

Vous ne devriez pas utiliser

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

vous devez utiliser simplement

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

En ce qui concerne les rêves liés à votre personne, vous n'avez pas besoin d'une demande d'extraction supplémentaire. Une fois que vous avez votre objet personne, vous suffit d'accéder à des rêves de cette personne comme suit:

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

Enfin, on ne sait pas pourquoi vous ne passez pas explicitement le contexte de l'objet géré à votre DreamViewController comme une variable d'instance. C'est une pratique courante, également indiquée dans les exemples de codes Apple. Une autre erreur vérifie

if (fetchedObjects == nil) 

car il est légal de renvoyer zéro si la requête n'a trouvé aucun objet; vous devez plutôt vérifier si votre objet NSError n'est pas nul (vous devez l'initialiser à zéro avant d'exécuter votre demande de récupération):

if(error) 

La déclaration

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

peut même planter votre application, comme expliqué à la le début de ma réponse.

+0

Merci :) C'était très utile. J'ai pris quelques heures et j'ai relu les simples exemples «une entité» de ma documentation sur les livres et les pommes. Ensuite, passa aux exemples d'entités multiples avec votre réponse dans le dos de ma tête. Ensuite, il a finalement fait sens. Je passe maintenant la « personne sélectionnée » à mon DreamViewController, parce qu'un objet a toujours une référence à sa ManagedObjectContext Donc DreamViewController je peux mettre ObjectContext comme ceci: NSManagedObjectContext * = contexte self.selectedPerson.managedObjectContext; Je peux évoluer et comprendre cette approche. Merci encore. – RickiG

+0

Vous avez raison, un objet a toujours une référence à son ManagedObjectContext (c'est une propriété). Ma suggestion pour passer le NSManagedObjectContext au contrôleur de vue est juste plus générale et fonctionne dans tous les cas où vous ne passez pas un NSManagedObject au contrôleur mais le contrôleur doit créer un nouveau NSManagedObject pendant son action. –

+0

Il est sorti faux :) Bien sûr, je suis d'accord que le fait de passer le ManagedObjectContext est la façon sûre et pérenne de le faire. Je suppose que c'est un fantôme de mon Java/Flash en pensant que vous essayez d'éviter de passer un "modèle" autour de chaque contrôleur possible, que ce soit un singleton ou non. Juste pour être sûr que j'ai bien compris; mon self.selectedPerson.managedObjectContext est le contexte des personnes (nom de la personne, rêves) et non le modèle entier? Si j'avais réussi le ManagedObjectContext original j'aurais eu accès au modèle entier (d'autres personnes et leurs rêves)? – RickiG

Questions connexes