私は本当に厄介な問題を抱えており、それを直せないようです。
Core Dataに保存されるメッセージを送信すると、データベースにランダムメッセージ(文)を要求し、それをデータベースの他の行に保存するビューがあります。
DBからデータをフェッチせずにハードコーディングされた最後の部分を行うと、うまく機能しますが、DBからランダムな行をフェッチするとすぐに狂ってしまいます。
私の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;
}
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];
}
私の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];
}
エラー:
2012-08-10 00:28:20.526チャット[13170:c07] *-[AppDelegate save]でのアサーションエラー、/ Users/paulp/Desktop/TestTask/Classes/AppDelegate.m:28 2012-08- 10 00:28:20.527 Chat [13170:c07] *キャッチされていない例外 'NSInternalInconsistencyException'によるアプリの終了、理由: '保存に失敗しました{type = immutable dict、count = 2、entries => 1:{contents = " NSAffectedObjectsErrorKey "} =("(エンティティ:センテンス; id:0x6b8bf10;データ:) ")2:{contents =" NSUnderlyingException "} = CoreDataは '0x6b8bf10'のフォールトを満たせませんでした}
私は何を間違えていますか?
更新この行のエラーを特定しました:
NSArray *sentenceArray = [self.context executeFetchRequest:request error:&error];
その行を実行すると、エラーが発生します...それはデータをフェッチするときです。ただし、新しいデータをメッセージエンティティに保存すると、エラーが発生するようです。ランダムな文は文から取得されます。
AsyncSaveメソッドを直接保存に変更した後(新しいスレッドを使用しない)、最初のチャットは保存されますが、その後は何も保存されません。死ぬ。
更新それはすべてdidFinishLaunchingWithOptions
でこれを使用して動作するようです:
[self.context setRetainsRegisteredObjects:YES];
これにより、CodeData Object Model Contextはオブジェクトを解放しないことを理解しています。これは、追加と保存の間の問題のようです。しかし、なぜ?
概念的に「save」コアデータオブジェクト「を異なる行に」。コアデータはオブジェクトグラフであり、データベースではないことに注意してください。
文を「再配置」する場合、最良の方法は文を破棄して再作成することです。古いインスタンスを保持する場合は、新しいインスタンスを作成してから、既存のインスタンスのプロパティを入力します。
破壊するには、
[self.context deleteObject:sentenceObject];
再作成するには、
Sentence *newSentence = [NSEntityDescription insertNewObjectForEntityForName:
"Sentences" inManagedObjectContext:self.context];
newSentence.sentence = sentenceObject.sentence;
// fill in other properties, then
[self.context save:error];
これについて詳しく知りたい場合は、「Core Data Programming Guide」の「Using Managed Objects」セクションの「 Copy and Copy and Paste 」を参照してください。
うーん。 このガイド に従って並行性を適切に実装していますか?発生している問題は、複数のスレッドでコアデータを使用する場合によくある問題です。オブジェクトは「バックグラウンドコンテキスト」で削除されましたが、別のコンテキストからアクセスされています。削除後、保存前にバックグラウンドコンテキストで[context processPendingChanges]
を呼び出すと役立つ場合があります。
コアデータパフォーマンスの最適化に関するWWDC 2010セッション(137)もありますが、これは少し削除されます。
フェッチを実行すると、Core Dataは指定した述語に一致するオブジェクトのコレクションを返します。これらのオブジェクトには、実際にはまだプロパティ値が設定されていません。プロパティにアクセスすると、Core Dataがストアに戻って「フォールトを起動」し、ストアからのデータをプロパティに入力します。 Core Dataがストアにアクセスしてオブジェクトのプロパティ値を取得しても、オブジェクトが永続ストアに存在しない場合、「障害を解決できませんでした...」例外が発生します。管理対象オブジェクトのコンテキストthoughtが存在する必要があるため、障害が発生する可能性があります-問題がある場所です。例外がスローされる原因となったコンテキストは、このオブジェクトが他の何か(別のコンテキストなど)によってストアから削除されたことを知りませんでした。
上記の同時実行ガイドは古くなっていることに注意してください。古いスレッド制限モデルではなく、親子コンテキストとプライベートキューの同時実行を使用する必要があります。親子コンテキストは、多くの理由で「障害を解決できませんでした...」に陥る可能性がはるかに低くなります。そして、ドキュメントのバグを提出するか、フィードバックフォームを使用して、同時実行ガイドの更新をリクエストしてください。
コアデータメカニズムを確認します。 「フォールトは、アプリケーションが消費するメモリ量を削減します。フォールトは、まだ完全には実現されていない管理対象オブジェクトを表すプレースホルダーオブジェクト、または関係を表すコレクションオブジェクトです。」
これは、最初の呼び出しのすべてのリレーションの取得が完了する前に、新しい行に「ランダムメッセージ」を追加しているために発生します。
遅延読み込みを回避するために、最初の呼び出しにプリフェッチを追加すると、問題が解決されると思います。
これは、リクエストのプリフェッチを行う方法です。
[request setRelationshipKeyPathsForPrefetching:[NSArray arrayWithObjects:@"whatEverOfYourWillNumberOne",@"whatEverOfYourWillNumberTwo", nil]];
お役に立てば幸いです。
NSFetchedResultsControllerの「cacheName」文字列をnilに変更することでエラーを修正しました。
NSFetchedResultsController * aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:@"ルート"nil];