CoreData ne pouvait pas répondre à une faute pour
J'ai vraiment un problème gênant, que je juste ne pouvez pas sembler obtenir fixe.
J'ai vue quand j'envoie un message est enregistré sur la Base de Données, lorsque c'est fait, il a demandé à la base de données pour un message aléatoire (phrase) et est enregistré aussi bien à une autre ligne dans la base de données.
Si je fais la dernière partie codée en dur, sans extraction de données à partir de la DB, il travaille tous très bien et dandy, mais dès que je récupère la ligne aléatoire à partir de la DB, il 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 Mon 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];
}
Référentiel.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 d'Assertion dans
-[AppDelegate enregistrer], /Users/paulp/Desktop/TestTask/Classes/AppDelegate.m:28 2012-08-10
00:28:20.527 Chat[13170:c07] * Résiliation d'application en raison de uncaught
exception NSInternalInconsistencyException', la raison: "Enregistrer échoué
{type = immuable dict, count = 2,
entrées => 1 : {contenu =
"NSAffectedObjectsErrorKey"} = (
"(entité: Phrases; id: 0x6b8bf10 ;
données: )" ) 2 : {contenu =
"NSUnderlyingException"} = CoreData ne pouvait pas répondre à une faute pour
'0x6b8bf10
'}
Ce que je fais mal?
Mise à jour
J'ai pointé l'erreur à cette ligne:
NSArray *sentenceArray = [self.context executeFetchRequest:request error:&error];
Lorsque j'exécute la ligne, j'obtiens l'erreur... c'est lors de l'extraction de données. L'erreur, cependant, semble tourner lors de l'enregistrement de nouvelles données pour les Messages de l'entité. Le hasard phrase est extraite à partir des Phrases.
Après j'ai changé le asyncSave méthode pour l'enregistrement direct (donc pas à l'aide d'un nouveau thread), il enregistre le premier chat, mais rien après. Il meurt.
Mise à jour
Tout semble fonctionner à l'aide de ce dans mon didFinishLaunchingWithOptions
:
[self.context setRetainsRegisteredObjects:YES];
Je comprends que, par la présente, le CodeData Modèle d'Objet de Contexte n'a pas communiqué ses objets, ce qui semble être le problème entre l'ajout et de l'épargne. Mais pourquoi?
- Paul, dans votre mise à jour vous mentionner que vous avez identifié votre erreur
executeFetchRequest
appel encore votre erreur survient lors de l'enregistrement de votre contexte qui n'a pas de sens. Aucune chance de l'utilisation de plusieurs threads dans votre application? C'est très souvent coupable pourNSInternalInconsistencyException
- Quelle est la différence entre le soi.contexte et l'auto.managedObjectContext. Vous semblez être les utiliser de façon interchangeable, et qui ne sonne pas comme une bonne idée.
- Aussi, il semble que vous n'êtes pas à l'aide de l'ARC, mais il ne semble pas que vous êtes la bonne gestion de votre mémoire. Cette erreur signifie généralement que vous avez supprimé quelque chose de la boutique, mais ne s'est pas correctement mise à jour de votre MOC, ou vous avez plusieurs threads de jouer avec le même MOC.
- Auto.le contexte n'est rien de plus qu'une méthode dans ma AppDelegate. J'ai analysé et vérifié mon code, mais je n'ai pas de fuites de mémoire là. Comment puis-je m'assurer que je suis sur le même thread?
- ... J'ai mis à jour la question. Aussi, pour expliquer mon dernier commentaire: je n'ai pas de fuites à tous. Ni puis-je obtenir toute analizy ou des avertissements lors de la construction.
- ce que @PeterPajchl dit: est-ce votre application multi-thread? Êtes-vous accéder à votre Base de Données gérée contexte de l'objet à partir de plusieurs threads? (Oh oh!)
- J'étais avant, mais j'ai supprimé cette partie. Le asyncsave fait un preformselector avec un délai de 0, ce qui a retardé l'enregistrer dans un nouveau thread. Autant que je sache, et peut le voir, je ne suis pas à l'aide de threads.
- aussi, au lieu de
arc4random() % count
, vous pouvez utiliserarc4random_uniform( count )
. Il est plus correct, non pas que cela importe. - Avant votre accès ajouter ceci:
assert( [ NSThread currentThread ] == [ NSThread mainThread ] )
- .. Ok, je devrais le faire à la fois lors de la lecture et de l'économie? Si ce n'est pas le même, il sera jeté une exception dans le journal... suis-je le droit?
- aussi, vous avez un OBOB (désactivé par un bug) je crois: (comte - arc4random() % nombre) peut être l'un de haut. Utilisation
count - 1 - arc4random() % count
- oui--non pas sur le thread principal va s'écraser... Vous pouvez utiliser NSAssert() lève une exception à la place..
- Ok. Je vais essayer un peu. Si je ne suis pas sur le thread principal, comment puis-je m'assurer que je serai? Bien sûr, une partie de la suppression de preformeSelector ect.
- laissez-nous continuer cette discussion dans le chat
Vous devez vous connecter pour publier un commentaire.
Il n'est pas vraiment conceptuellement possible de "enregistrer" Base de Données des objets "dans des lignes différentes". Rappelez-vous, l'essentiel des Données est un objet graphique et non une base de données.
Si vous voulez "relocaliser" votre phrase, le meilleur moyen est de le détruire et de le recréer. Si vous voulez garder l'ancienne instance, il suffit de créer une nouvelle, puis remplissez les propriétés à partir de l'existant.
Pour la destruction, l'utilisation
Pour recréer, à l'utilisation
Si vous souhaitez lire sur ce, regardez "La copie, Copier et Coller" dans la section "Utilisation des Objets Gérés" de la section "Base de Données Guide de Programmation".
Hmm. Êtes-vous correctement la mise en œuvre de la simultanéité suivant ce guide?
Le problème que vous rencontrez est une commune lors de l'utilisation de base de données entre plusieurs threads. L'objet a été supprimé dans votre "contexte", puis il est utilisé par un autre contexte. L'appel de
[context processPendingChanges]
sur votre arrière-plan contexte après la supprimer, mais avant l'enregistrement peut aider.Il y a aussi un WWDC 2010 session (137) sur l'optimisation de base de données de performance qui va en supprime un peu.
Lorsque vous exécutez une extraction de Base de Données retourne une collection d'objets correspondant au prédicat que vous avez fournies. Ces objets n'ont pas réellement de la valeur de leur propriété encore fixé. C'est lorsque vous accéder à une propriété de Base de Données remonte à la boutique de "feu la faute" - remplir de la propriété avec les données de la banque. "Ne pourrait pas remplir une faute..." exceptions se produisent lorsque la Base de Données va au magasin pour obtenir les valeurs des propriétés d'un objet, mais l'objet n'existe pas dans le système de persistance. La gestion du contexte de l'objet pensée il doit exister, c'est pourquoi il pourrait tenter de la faute - qui est où est le problème. Le contexte qui a provoqué l'exception ne savais pas que cet objet a été supprimé à partir de la boutique par autre chose (comme un autre contexte).
Note le le au-dessus de la Simultanéité Guide est maintenant à jour, vous devriez être en utilisant parent-enfant contextes et privé de la file d'attente de la simultanéité plutôt que l'ancien fil de confinement modèle. Parent-enfant, les contextes sont beaucoup moins susceptibles d'exécuter en ", ne Pouvait pas répondre à une faute..." pour de nombreuses raisons. Et s'il vous plaît, fichier de documentation bug ou d'utiliser le formulaire de demande que la simultanéité guide sera mis à jour.
vérifier la base de données du mécanisme. "Défaillant réduit la quantité de mémoire de votre application consomme. Un défaut est un espace réservé à l'objet qui représente un objet géré qui n'a pas encore été pleinement réalisé, ou un objet de collection qui représente une relation:"
Ceci se produit en raison de l'ajout du "message aléatoire" de votre nouvelle ligne avant la fin de la récupération de toutes vos relations du premier appel.
Vous pouvez ajouter une récupération pour votre 1er appel d'éviter le chargement paresseux et le problème sera résolu, je crois.
C'est de cette façon que nous pouvons faire pour le pré-chargement de la demande:
Espère que ça aide.
J'ai corrigé l'erreur avec l'évolution de la NSFetchedResultsController du "cacheName" chaîne à néant.
NSFetchedResultsController *aFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:auto.managedObjectContext sectionNameKeyPath:néant
cacheName:
@"Root"nil];