J'ai un problème vraiment ennuyeux, que je n'arrive pas à résoudre.
J'ai une vue lorsque j'envoie un message qui est enregistré dans les données de base, lorsque cela est fait, il a demandé à la base de données un message aléatoire (phrase) et l'a également enregistré sur une autre ligne de la base de données.
Si je fais la dernière partie codée en dur, sans récupérer les données de la base de données, cela fonctionne très bien et dandy, mais dès que je récupère la ligne aléatoire de la base de données, cela devient fou.
Dans mon AppDelegate.m:
- (void)save {
NSAssert(self.context != nil, @"Not initialized");
NSError *error = nil;
BOOL failed = [self.context hasChanges] && ![self.context save:&error];
NSAssert1(!failed,@"Save failed %@",[error userInfo]);
}
- (NSString*)selectRandomSentence
{
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Sentences" inManagedObjectContext:self.managedObjectContext];
[request setEntity:entity];
NSError *error = nil;
NSUInteger count = [self.context countForFetchRequest:request error:&error];
NSUInteger offset = count - (arc4random() % count);
[request setFetchOffset:offset];
[request setFetchLimit:1];
NSArray *sentenceArray = [self.context executeFetchRequest:request error:&error];
[request release];
return [[sentenceArray objectAtIndex:0] sentence];
}
- (NSManagedObjectContext *)context {
if (_managedObjectContext != nil)
return _managedObjectContext;
NSPersistentStoreCoordinator *coordinator = [self coordinator];
if (coordinator != nil) {
_managedObjectContext = [[NSManagedObjectContext alloc] init];
[_managedObjectContext setPersistentStoreCoordinator:coordinator];
}
return _managedObjectContext;
}
Dans mon ChatController.m:
- (void)didRecieveMessage:(NSString *)message
{
[self addMessage:message fromMe:NO];
}
#pragma mark -
#pragma mark SendControllerDelegate
- (void)didSendMessage:(NSString*)text {
[self addMessage:text fromMe:YES];
}
#pragma mark -
#pragma mark Private methods
- (void)responseReceived:(NSString*)response {
[self addMessage:response fromMe:NO];
}
- (void)addMessage:(NSString*)text fromMe:(BOOL)fromMe {
NSAssert(self.repository != nil, @"Not initialized");
Message *msg = [self.repository messageForBuddy:self.buddy];
msg.text = text;
msg.fromMe = fromMe;
if (fromMe)
{
[self.bot talkWithBot:text];
}
[self.repository asyncSave];
[self.tableView reloadData];
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:[self.buddy.messages count] - 1] atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}
Dans My OfflineBot.m:
- (void)talkWithBot:(NSString *)textFromMe
{
AppDelegate *delegate = [[UIApplication sharedApplication] delegate];
[self didRecieveMessage:[delegate selectRandomSentence]];
}
- (void)didRecieveMessage:(NSString *)message
{
if ([self.delegate respondsToSelector:@selector(didRecieveMessage:)])
[self.delegate didRecieveMessage:message];
}
Repository.m
- (Message*)messageForBuddy:(Buddy*)buddy {
Message *msg = [self.delegate entityForName:@"Message"];
msg.source = buddy;
[self.delegate.managedObjectContext refreshObject:buddy mergeChanges:YES];
return msg;
}
- (void)asyncSave {
[self.delegate save];
}
L'erreur:
2012-08-10 00: 28: 20.526 Chat [13170: c07] * Échec de l'assertion dans - [AppDelegate save], /Users/paulp/Desktop/TestTask/Classes/AppDelegate.m:28 2012-08- 10 00: 28: 20.527 Chat [13170: c07] * Arrêt de l'application en raison de l'exception non interceptée 'NSInternalInconsistencyException', raison: 'Échec de la sauvegarde {type = dict immuable, nombre = 2, entrées => 1: {contenu = " NSAffectedObjectsErrorKey "} = (" (entity: Sentences; id: 0x6b8bf10; data:) ") 2: {contents =" NSUnderlyingException "} = CoreData n'a pas pu corriger une erreur pour '0x6b8bf10'}
Qu'est-ce que je fais mal?
Mise à jour J'ai localisé l'erreur sur cette ligne:
NSArray *sentenceArray = [self.context executeFetchRequest:request error:&error];
Lorsque j'exécute cette ligne, j'obtiens l'erreur ... c'est lors de la récupération des données. L'erreur, cependant, semble se produire lors de l'enregistrement des nouvelles données dans l'entité Messages. La phrase aléatoire est extraite des phrases.
Après avoir modifié la méthode asyncSave en enregistrant directement (n'utilisant donc pas de nouveau thread), elle enregistre le premier chat, mais rien après. Il meurt.
Mise à jour Tout semble fonctionner en utilisant ceci dans mon didFinishLaunchingWithOptions
:
[self.context setRetainsRegisteredObjects:YES];
Je comprends que le contexte du modèle d'objet CodeData ne libère pas ses objets, ce qui semble être le problème entre l'ajout et l'enregistrement. Mais pourquoi?
Il n'est pas vraiment possible conceptuellement " d'enregistrer " les objets Core Data " dans différentes lignes ". N'oubliez pas que Core Data est un graphique d'objet et non une base de données.
Si vous voulez "déplacer" votre phrase, la meilleure façon est de la détruire et de la recréer. Si vous souhaitez conserver l'ancienne instance, il vous suffit d'en créer une nouvelle, puis de remplir les propriétés de l'existante.
Pour détruire, utilisez
[self.context deleteObject:sentenceObject];
Pour recréer, utilisez
Sentence *newSentence = [NSEntityDescription insertNewObjectForEntityForName:
"Sentences" inManagedObjectContext:self.context];
newSentence.sentence = sentenceObject.sentence;
// fill in other properties, then
[self.context save:error];
Si vous souhaitez en savoir plus, consultez " Copier et copier-coller " dans la section "Utilisation des objets gérés" du "Guide de programmation des données de base".
Hmm. Implémentez-vous correctement la simultanéité en suivant ce guide ? Le problème que vous voyez est un problème courant lors de l'utilisation de données de base sur plusieurs threads. L'objet a été supprimé dans votre "contexte d'arrière-plan", alors qu'il est accessible par un autre contexte. Appeler [context processPendingChanges]
sur votre contexte d'arrière-plan après la suppression mais avant l'enregistrement peut vous aider.
Il existe également une session WWDC 2010 (137) sur l'optimisation des performances des données de base qui va un peu dans les suppressions.
Lorsque vous exécutez une extraction, Core Data renvoie une collection d'objets correspondant au prédicat que vous avez fourni. Ces objets n'ont pas encore leurs valeurs de propriété définies. C'est lorsque vous accédez à une propriété que Core Data retourne au magasin pour "déclencher la faute" - remplissez la propriété avec les données du magasin. Des exceptions "Impossible de résoudre une erreur ..." se produisent lorsque Core Data se rend dans le magasin pour obtenir les valeurs de propriété d'un objet, mais que l'objet n'existe pas dans le magasin persistant. Le contexte de l'objet géré pensait qu'il devrait exister, c'est pourquoi il pourrait tenter la faute - c'est là que se situe le problème. Le contexte qui a provoqué la levée de l'exception ne savait pas que cet objet avait été supprimé du magasin par autre chose (comme un autre contexte).
Notez que le guide de concurrence ci-dessus est désormais obsolète, vous devez utiliser les contextes parent-enfant et la concurrence de file d'attente privée plutôt que l'ancien modèle de confinement des threads. Les contextes parent-enfant sont beaucoup moins susceptibles de se heurter à "N'a pas pu combler une erreur ..." pour de nombreuses raisons. Veuillez également signaler un bogue de documentation ou utiliser le formulaire de commentaires pour demander la mise à jour du guide d'accès simultané.
vérifier le mécanisme de données de base. "Les défaillances réduisent la quantité de mémoire consommée par votre application. Une défaillance est un objet d'espace réservé qui représente un objet géré qui n'a pas encore été entièrement réalisé, ou un objet de collection qui représente une relation:"
Cela se produit car vous ajoutez le "message aléatoire" à votre nouvelle ligne avant la fin de la récupération de toutes vos relations du premier appel.
Vous pouvez ajouter une prélecture à votre premier appel pour éviter le chargement paresseux et le problème sera résolu, je crois.
Voici comment nous pouvons effectuer la prélecture de la demande:
[request setRelationshipKeyPathsForPrefetching:[NSArray arrayWithObjects:@"whatEverOfYourWillNumberOne",@"whatEverOfYourWillNumberTwo", nil]];
J'espère que cela pourra aider.
J'ai corrigé l'erreur en changeant la chaîne "cacheName" de NSFetchedResultsController à zéro.
NSFetchedResultsController * aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest: fetchRequest managedObjectContext: self.managedObjectContext sectionNameKeyPath: nil cacheName: (@"Racine"nul];