[解決済み] 「executeFetchRequestで「コレクションは列挙中に変異しました。
質問
私は今何時間も問題に行き詰っていて、stackoverflowでこのことに関するすべてを読みました(そして見つかったすべてのアドバイスを適用しました)。
ここにコンテキストがあります。
私のiPhoneプロジェクトでは、バックグラウンドでデータをインポートし、管理されたオブジェクトのコンテキストにそれを挿入する必要があります。ここで見つけたアドバイスに従って、私がやっていることは以下のとおりです。
- メインMOCを保存する
- メインmocが使用する永続ストア・コーディネータでバックグラウンドmocをインスタンス化する
- バックグラウンドモックのNSManagedObjectContextDidSaveNotification通知のオブザーバーとして、私のコントローラを登録します。
- バックグラウンドのスレッドでimportメソッドを呼び出す
- データを受信するたびに、バックグラウンドのmocにデータを挿入する
- すべてのデータをインポートしたら、背景のMOCを保存する。
- メインスレッドで、変更をメインモックにマージします。
- 私のコントローラをnotificationのオブザーバーとして登録解除します。
- バックグラウンドのMOCをリセットして解放する
時々(ランダムに)例外が...
*** Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <__NSCFSet: 0x5e0b930> was mutated while being enumerated...
...は、インポートされたデータがすでにデータベースに存在するかどうかをチェックするために、バックグラウンドmocでexecuteFetchRequestを呼び出したときにスローされます。インポートメソッドの外では何も実行されないので、何がセットを変異させているのでしょうか。
私は私のコントローラと私のテストエンティティ(これらの2つのクラスとアプリデリゲートからなる私のプロジェクトは、変更されていない)の全体のコードを含んでいます。
//
// RootViewController.h
// FK1
//
// Created by Eric on 09/08/10.
// Copyright (c) 2010 __MyCompanyName__. All rights reserved.
//
#import <CoreData/CoreData.h>
@interface RootViewController : UITableViewController <NSFetchedResultsControllerDelegate> {
NSManagedObjectContext *managedObjectContext;
NSManagedObjectContext *backgroundMOC;
}
@property (nonatomic, retain) NSManagedObjectContext *managedObjectContext;
@property (nonatomic, retain) NSManagedObjectContext *backgroundMOC;
@end
//
// RootViewController.m
// FK1
//
// Created by Eric on 09/08/10.
// Copyright (c) 2010 __MyCompanyName__. All rights reserved.
//
#import "RootViewController.h"
#import "FK1Message.h"
@implementation RootViewController
@synthesize managedObjectContext;
@synthesize backgroundMOC;
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationController.toolbarHidden = NO;
UIBarButtonItem *refreshButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh target:self action:@selector(refreshAction:)];
self.toolbarItems = [NSArray arrayWithObject:refreshButton];
}
#pragma mark -
#pragma mark ACTIONS
- (void)refreshAction:(id)sender {
// If there already is an import running, we do nothing
if (self.backgroundMOC != nil) {
return;
}
// We save the main moc
NSError *error = nil;
if (![self.managedObjectContext save:&error]) {
NSLog(@"error = %@", error);
abort();
}
// We instantiate the background moc
self.backgroundMOC = [[[NSManagedObjectContext alloc] init] autorelease];
[self.backgroundMOC setPersistentStoreCoordinator:[self.managedObjectContext persistentStoreCoordinator]];
// We call the fetch method in the background thread
[self performSelectorInBackground:@selector(_importData) withObject:nil];
}
- (void)_importData {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(backgroundMOCDidSave:) name:NSManagedObjectContextDidSaveNotification object:self.backgroundMOC];
FK1Message *message = nil;
NSFetchRequest *fetchRequest = nil;
NSEntityDescription *entity = [NSEntityDescription entityForName:@"FK1Message" inManagedObjectContext:self.backgroundMOC];
NSPredicate *predicate = nil;
NSArray *results = nil;
// fake import to keep this sample simple
for (NSInteger index = 0; index < 20; index++) {
predicate = [NSPredicate predicateWithFormat:@"msgId == %@", [NSString stringWithFormat:@"%d", index]];
fetchRequest = [[[NSFetchRequest alloc] init] autorelease];
[fetchRequest setEntity:entity];
[fetchRequest setPredicate:predicate];
// The following line sometimes randomly throw the exception :
// *** Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <__NSCFSet: 0x5b71a00> was mutated while being enumerated.
results = [self.backgroundMOC executeFetchRequest:fetchRequest error:NULL];
// If the message already exist, we retrieve it from the database
// If it doesn't, we insert a new message in the database
if ([results count] > 0) {
message = [results objectAtIndex:0];
}
else {
message = [NSEntityDescription insertNewObjectForEntityForName:@"FK1Message" inManagedObjectContext:self.backgroundMOC];
message.msgId = [NSString stringWithFormat:@"%d", index];
}
// We update the message
message.updateDate = [NSDate date];
}
// We save the background moc which trigger the backgroundMOCDidSave: method
[self.backgroundMOC save:NULL];
[[NSNotificationCenter defaultCenter] removeObserver:self name:NSManagedObjectContextDidSaveNotification object:self.backgroundMOC];
[self.backgroundMOC reset]; self.backgroundMOC = nil;
[pool drain];
}
- (void)backgroundMOCDidSave:(NSNotification*)notification {
if (![NSThread isMainThread]) {
[self performSelectorOnMainThread:@selector(backgroundMOCDidSave:) withObject:notification waitUntilDone:YES];
return;
}
// We merge the background moc changes in the main moc
[self.managedObjectContext mergeChangesFromContextDidSaveNotification:notification];
}
@end
//
// FK1Message.h
// FK1
//
// Created by Eric on 09/08/10.
// Copyright 2010 __MyCompanyName__. All rights reserved.
//
#import <CoreData/CoreData.h>
@interface FK1Message : NSManagedObject
{
}
@property (nonatomic, retain) NSString * msgId;
@property (nonatomic, retain) NSDate * updateDate;
@end
//
// FK1Message.m
// FK1
//
// Created by Eric on 09/08/10.
// Copyright 2010 __MyCompanyName__. All rights reserved.
//
#import "FK1Message.h"
@implementation FK1Message
#pragma mark -
#pragma mark PROPERTIES
@dynamic msgId;
@dynamic updateDate;
@end
これで全部です! プロジェクト全体がここにあります。テーブルビューもNSFetchedResultsControllerもなく、バックグラウンドでmocにデータをインポートするスレッド以外には何もありません。
この場合、何がセットを変更することができますか?
私は明らかに何かを見逃しているようで、気が狂いそうです。
EDITです。
以下は、スタックトレースの全容です。
2010-08-10 10:29:11.258 FK1[51419:1b6b] *** Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <__NSCFSet: 0x5d075b0> was mutated while being enumerated.<CFBasicHash 0x5d075b0 [0x25c6380]>{type = mutable set, count = 0,
entries =>
}
'
*** Call stack at first throw:
(
0 CoreFoundation 0x0255d919 __exceptionPreprocess + 185
1 libobjc.A.dylib 0x026ab5de objc_exception_throw + 47
2 CoreFoundation 0x0255d3d9 __NSFastEnumerationMutationHandler + 377
3 CoreData 0x02287702 -[NSManagedObjectContext executeFetchRequest:error:] + 4706
4 FK1 0x00002b1b -[RootViewController _fetchData] + 593
5 Foundation 0x01d662a8 -[NSThread main] + 81
6 Foundation 0x01d66234 __NSThread__main__ + 1387
7 libSystem.B.dylib 0x9587681d _pthread_start + 345
8 libSystem.B.dylib 0x958766a2 thread_start + 34
)
terminate called after throwing an instance of 'NSException'
どのように解決するのですか?
OK、私は私が私の問題を解決したと思うし、私はフレッドマッキャンからこのブログの記事に感謝しなければならない:
http://www.duckrowing.com/2010/03/11/using-core-data-on-multiple-threads/
この問題は、バックグラウンドスレッドではなくメインスレッドでバックグラウンドモックをインスタンス化していることに起因しているようです。Apple が各スレッドが独自の moc を持つ必要があると言った場合、それを真剣に受け止めなければなりません。
次の行を移動する...
// We instantiate the background moc
self.backgroundMOC = [[[NSManagedObjectContext alloc] init] autorelease];
[self.backgroundMOC setPersistentStoreCoordinator:[self.managedObjectContext persistentStoreCoordinator]];
..._importDataメソッド(notificationのオブザーバーとしてコントローラを登録する直前)で問題を解決します。
Peterさん、ありがとうございました。そしてFred McCann'sの貴重なブログ記事に感謝します !
関連
-
[解決済み】Not on FX application thread; currentThread = JavaFX Application Thread エラーを回避する方法は?
-
[解決済み] Dask: dask delayでどのようにコードを並列化するか?
-
[解決済み] POSIXシステムでのゾンビスレッド
-
[解決済み] "フリースレッド "と "スレッドセーフ "の違いについて
-
[解決済み] Win32 InterlockedExchange関数はどのような場合に使用するのですか?
-
[解決済み] デッドロックとは何ですか?
-
[解決済み] Pythonでループ内の演算をマルチスレッド化する方法
-
[解決済み】セマフォとモニター、何が違うの?
-
[解決済み】並行処理、並列処理、非同期メソッドの違いは何ですか?
-
[解決済み】Redisはシングルスレッドですが、同時I/Oはどのように行うのですか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] Dask: dask delayでどのようにコードを並列化するか?
-
[解決済み] POSIXシステムでのゾンビスレッド
-
[解決済み] "フリースレッド "と "スレッドセーフ "の違いについて
-
[解決済み] 初心者のためのアトミック操作とは?
-
[解決済み] Pythonでループ内の演算をマルチスレッド化する方法
-
[解決済み】「スレッド」とは(本当は)何ですか?)
-
[解決済み】Node.jsに対するHaskellの対応について教えてください。
-
[解決済み】糸と繊維の違いは何ですか?
-
[解決済み】Redisはシングルスレッドですが、同時I/Oはどのように行うのですか?
-
[解決済み] Re-entrantロックとは何ですか?