3

GCDカスタムNSManagedObjectsと組み合わせたの誤った実装が原因のように見えるランタイムエラーが発生します。

呼び出しにネストされGCDて、独自の管理対象オブジェクトコンテキスト(= self.managedObjectContext)を持つ(と思われる)カスタムNSManagedObjectsを使用しています。

UIManagedDocument:によって提供される管理対象オブジェクトコンテキストを使用して、アプリデリゲートで管理対象オブジェクトコンテキストを作成していますself.managedDocument.managedObjectContext

正しい管理対象オブジェクトコンテキストをカスタムNSManagedObjectsに渡す方法がわかりません。正しい管理対象オブジェクトコンテキストを使用するには、コードをどのように変更する必要がありますか?

これが私の主な方法です(ビューコントローラー内):

dispatch_queue_t queue;
queue = dispatch_queue_create("queue", NULL);
dispatch_async(queue, ^{
// ...
NSDecimalNumber *value = [reportedPeriod 
   valueForCoa:figure.code 
   convertedTo:self.currencySymbol];
// ...});
}

このメインメソッドでは、管理対象オブジェクトコンテキストへの参照はなく、呼び出すだけですvalueForCoa:convertedTo:(次のようにコード化されています)。

- (NSDecimalNumber*)valueForCoa:(NSString*)coaStr
convertedTo:(NSString*)targetCurrencyStr {
// ...
CoaMap *coa = [CoaMap coaItemForString:coaStr
   inManagedObjectContext:self.managedObjectContext];
// ...
}

valueForCoaカスタムサブクラス化されたNSManagedObjectのメソッドであり、ReportedPeriodその(デフォルトの)管理対象オブジェクトコンテキストを使用しますself.managedObjectContext

CoaMapその後、アプリは通常、フェッチリクエストを実行するときに、次のメソッドのカスタムサブクラス化されたNSManagedObjectでクラッシュします。

+ (CoaMap*)coaItemForString:(NSString*)coaStr 
inManagedObjectContext:(NSManagedObjectContext*)context {

NSFetchRequest *request = [NSFetchRequest 
fetchRequestWithEntityName:NSStringFromClass([self class])];
NSPredicate *predicate = 
   [NSPredicate predicateWithFormat:@"coa == %@",coaStr];
   request.predicate = predicate;
// ** The runtime error occurs in the following line **
NSArray *results = [context executeFetchRequest:request error:nil];
// ...
}

エラーメッセージは次のとおりです。Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <__NSCFSet: 0x9a8a4a0> was mutated while being enumerated.

この問題について私を助けて、正しい管理対象オブジェクトコンテキストを渡すためにコードを改善する方法(またはすべてのメソッドで正しいコンテキストが使用されていることを確認する方法)についていくつかの提案をお願いします。

どうもありがとうございます!

4

2 に答える 2

7

このエラーは通常、管理対象オブジェクトを異なるスレッドまたはキュー間で誤ってコンテキストを使用することに関連しています。メインキューにMOCを作成しましたが、その事実を考慮せずにバックグラウンドキューで使用しています。バックグラウンドキューでMOCを使用することは間違いではありませんが、それを認識して準備する必要があります。

あなたはMOCをどのように作成しているのかを言いませんでした。私はあなたがこれをするべきであることを提案します:

NSManagedObjectContext *context = [[NSManagedObjectContext alloc]
    initWithConcurrencyType: NSMainQueueConcurrencyType];

メインキューの同時実行性を使用すると、メインスレッドで通常どおりに使用できます。ただし、ディスパッチキューにいるときは、次のようにします。

[context performBlockAndWait:^{
    NSFetchRequest *request = [NSFetchRequest 
        fetchRequestWithEntityName:NSStringFromClass([self class])];
    NSPredicate *predicate = 
       [NSPredicate predicateWithFormat:@"coa == %@",coaStr];
    request.predicate = predicate;
    NSArray *results = [context executeFetchRequest:request error:nil];
    // ...
}];

これにより、バックグラウンドキューにいる場合でも、MOCの作業がメインスレッドで確実に実行されます。(技術的には、バックグラウンドでのMOCの作業は、メインキューでの作業と正しく同期されますが、結果は同じです。これは、これを行うための安全な方法です)。

同様のアプローチは、NSPrivateQueueConcurrencyType代わりに使用することです。そうすれば、バックグラウンドスレッドだけでなく、MOCのためにperformBlockまたはどこでも使用することになります。performBlockAndWait

于 2013-02-01T17:12:11.550 に答える
0

初め、

「正しい管理対象オブジェクトコンテキストをカスタムNSManagedObjectsに渡す方法。」

で作成NSManagedObjectNSManagedObjectContextます。その逆ではありません。したがって、を持っている場合は、そのプロパティを尋ねるNSManagedObjectことでにアクセスできます: AppleDocumentにリストされているようにNSManagedObjectContext– managedObjectContext

2番、

CoreDataを使用する場合、マルチスレッドは少し注意が必要です。特に初心者のために。あなたが世話をする必要があるすべての種類の詳細です。

をチェックアウトすることを強くお勧めしますParent-Child NSManagedContext。次に、MagicRecordを使用します。

MagicRecordを使用すると、次のようなブロックを使用して、 GrandCentralDispatchを簡単に実行できます。

[MagicalRecord saveInBackgroundWithBlock:^(NSManagedObjectContext *localContext){

    // here use the `localContext` as your NSManagedContext and do things in the background.
    // it will take care of all the rest.

}];

NSManagedObjectこのブロックに渡す必要がある場合は、プロパティのNSManagedObjectID代わりに渡すことだけを忘れないでください。

これは一例です。

NSManagedObjectID *coaMapID = CoaMap.objectID;

[MagicalRecord saveInBackgroundWithBlock:^(NSManagedObjectContext *localContext){
    coaMap *aCoaMap = (coaMap *)[localContext existingObjectWithID:coaMapID error:&error];
    // then use aCoaMap normally.
}];

これがお役に立てば幸いです。

于 2013-02-01T17:21:00.720 に答える