次のように定義されたNSManagedObjectContextのGCDディスパッチキューで操作を行っています。
- (NSManagedObjectContext *)backgroundContext
{
if (backgroundContext == nil) {
self.backgroundContext = [NSManagedObjectContext MR_contextThatNotifiesDefaultContextOnMainThread];
}
return backgroundContext;
}
MR_contextThatNotifiesDefaultContextOnMainThread
MagicalRecordのメソッドです:
NSManagedObjectContext *context = [[self alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[context setParentContext:[NSManagedObjectContext MR_defaultContext]];
return context;
オブジェクトをフェッチして正しいキュー位置を指定した後、それらをログに記録し、順序は正しいです。ただし、2番目のログは完全にランダムであるように見え、ソート記述子は明らかに機能していません。
問題をに絞り込みました[self.backgroundContext save:&error]
。バックグラウンドコンテキストを保存した後、ソート記述子が壊れます。
dispatch_group_async(backgroundGroup, backgroundQueue, ^{
// ...
for (FooObject *obj in fetchedObjects) {
// ...
obj.queuePosition = [NSNumber numberWithInteger:newQueuePosition++];
}
NSFetchRequest *f = [NSFetchRequest fetchRequestWithEntityName:[FooObject entityName]];
f.predicate = [NSPredicate predicateWithFormat:@"queuePosition > 0"];
f.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:@"queuePosition" ascending:YES]];
NSArray *queuedObjects = [self.backgroundContext executeFetchRequest:f error:nil];
for (FooObject *obj in queuedObjects) {
DLog(@"%@ %@", obj.queuePosition, obj.title);
}
if ([self.backgroundContext hasChanges]) {
DLog(@"Changes");
NSError *error = nil;
if ([self.backgroundContext save:&error] == NO) {
DLog(@"Error: %@", error);
}
}
queuedObjects = [self.backgroundContext executeFetchRequest:f error:nil];
for (FooObject *obj in queuedObjects) {
DLog(@"%@ %@", obj.queuePosition, obj.title);
}
});
ソート記述子が機能しない理由がわかりません。コアデータの専門家が助けてくれませんか?
アップデート:
この問題はiOS4では発生しません。理由はスレッド分離モードとプライベートキューモードの違いにあると思います。MagicalRecordは、動作が異なるように見える新しい同時実行パターンを自動的に使用します。
アップデート2:
この問題は、バックグラウンドコンテキストの保存を追加することで解決されました。
if ([[NSManagedObjectContext MR_contextForCurrentThread] hasChanges]) {
DLog(@"Changes");
NSError *error = nil;
if ([[NSManagedObjectContext MR_contextForCurrentThread] save:&error] == NO) {
DLog(@"Error: %@", error);
} else {
NSManagedObjectContext *parent = [NSManagedObjectContext MR_contextForCurrentThread].parentContext;
[parent performBlockAndWait:^{
NSError *error = nil;
if ([parent save:&error] == NO) {
DLog(@"Error saving parent context: %@", error);
}
}];
}
}
アップデート3:
MagicalRecordは、コンテキストを再帰的に保存するメソッドを提供します。これで、私のコードは次のようになります。
if ([[NSManagedObjectContext MR_contextForCurrentThread] hasChanges]) {
DLog(@"Changes");
[[NSManagedObjectContext MR_contextForCurrentThread] MR_saveWithErrorHandler:^(NSError *error) {
DLog(@"Error saving context: %@", error);
}];
}
そもそも使わなかったのが恥ずかしい…
しかし、なぜこれが役立つのかわかりません。説明を求めています。