0

次のコードを使用して、xmlファイルを解析する関数を実行しています...

dispatch_queue_t queue = dispatch_queue_create("updateQueue", DISPATCH_QUEUE_CONCURRENT);

dispatch_async(queue,^ { [self updateFromXMLFile:@"http://path/to/file.xml"]; } ); 
dispatch_async(queue,^ { [self updateFromXMLFile:@"http://path/to/file1.xml"]; } );
dispatch_async(queue,^ { [self updateFromXMLFile:@"http://path/to/file2.xml"]; } );
dispatch_async(queue,^ { [self updateFromXMLFile:@"http://path/to/file3.xml"]; } );

dispatch_barrier_async(queue,^ {
    dispatch_async(dispatch_get_main_queue(),^ { 
            [self setBottomBarToUpdated]; 
    });
});

以下は関数updateFromXMLFileです:

- (BOOL) updateFromXMLFile:(NSString *)pathToFile {

         NSURL *url = [[NSURL alloc] initWithString:pathToFile];
         NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithContentsOfURL:url];

         XMLParser *parser = [[XMLParser alloc] initXMLParser];

         parser.managedObjectContext = self.managedObjectContext;

         [xmlParser setDelegate: parser];

         BOOL success = [xmlParser parse];

         if(success)
              return TRUE;
         else
              return FALSE;

}

私が遭遇している問題は、このエラーメッセージです。***Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <__NSCFSet: 0xc675e10> was mutated while being enumerated.'

それは、ManagedObjectContextを同時に操作するすべてのプロセスと関係があると思います。しかし、それをどのように処理するかはわかりません。何か案は?ありがとう!

4

2 に答える 2

1

XMLParser がデリゲートの責任を処理する方法のコードを表示する必要があります。それが呼び出されているからです。

これで、別々のスレッド内から同時に複数のファイルを解析しています。それらはすべて異なる XMLParser オブジェクトを呼び出していますが、それぞれが同じ管理対象オブジェクト コンテキスト (MOC) を使用しています。

デフォルトの MOC セットアップを使用している場合は、すべての MOC 呼び出しをメイン スレッドに再召集する必要があります (それが実際に最初にコンテキストを作成した場所である場合)。制限を使用していて、メイン スレッドで MOC が作成されていない場合は、さらに問題が発生します。

ただし、これは非常に簡単な修正です。そのパーサー デリゲート メソッドでは、マネージ オブジェクト コンテキストを使用するときは常に、それを呼び出しで囲み、メイン スレッドでその部分をディスパッチします。

dispatch_async(dispatch_get_main_queue(), ^{
    // Put your code that accesses the MOC in here.
});

ここで、コア データへの一括ダウンロードを行うには、おそらく 2 つの方法のいずれかを使用することをお勧めします。

永続ストア コーディネーターに直接アタッチされた新しい MOC を作成し、そこですべての保存を行います。メインの MOC は、保存通知を監視し、それらの変更をマージする必要があります。

または、新しい MOC をメインの MOC の子にして、そこに直接保存すると、保存できます。

ただし、管理対象オブジェクト コンテキストが NSMainQueueConcurrencyType または NSPrivateQueueConcurrency タイプの場合 (上記の 2 番目のオプションを実行する唯一の方法)、その perfromBlock メソッドを使用できます...

[managedObjectContext performBlock:^{
    // Do your MOC stuff in here
}];

要するに、MOC は 3 つの同時実行タイプのいずれかで作成できるということです。NSConfinementConcurrencyType の場合は、それが作成されたスレッドの外で触れてはなりません。NSMainQueueConcurrencyType の場合は、performBlock* を使用するか、メイン スレッドでのみタッチする必要があります。NSPrivateConcurrencyType の場合は、performBlock* を使用する必要があります。

于 2012-08-14T16:14:42.990 に答える
1

問題は、並列キューを使用して MOC に接続できないことです。シリアル キューを使用する必要があります。キューの作成を次のように変更します

dispatch_queue_t queue = dispatch_queue_create("updateQueue", DISPATCH_QUEUE_SERIAL);

そしてすべてがうまくいくでしょう。繰り返しますが、これを行う理由は、最終的な XMLParser オブジェクトが同時に moc と対話しようとしているためです。

オプションは、XMLParser (または任意のクラス) の動作によって決定されます。クラスが MOC と対話する前、または Web からデータをダウンロードする前に重い作業を行う場合、並行性によって何かを得ることができます。

私がお勧めするのは、URLS から必要なデータをダウンロードしてから、MOC を順次操作することです。単一のスレッド (iOS の場合) で MOC と対話する必要がありますが、それは mainThread である必要はありません。これは、独自のシリアル キューを作成する場合、MOC の作成を含め、すべてをそのキューで実行する必要があることを意味します。

コードに戻ります。すべての Core Data 作業を mainQueue で行うと仮定しましょう (今のところ)。解決策は、コードを次のように変更することです。

dispatch_async(dispatch_get_global_queue(0,0) {
  NSURL *url = [[NSURL alloc] initWithString:pathToFile];
  NSData *data = [NSData dataWithContentsOfURL:url];
  NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithData:data];
  XMLParser *parser = [[XMLParser alloc] initXMLParser];
  parser.managedObjectContext = self.managedObjectContext;
  [xmlParser setDelegate: parser];

  dispatch_async(dispatch_get_main_queue(), {
         BOOL success = [xmlParser parse];

         // need some means to associate success/failure with the URL
  } );

MOC にヒットしたときにシリアライズをできる限り並行して行います。MOC にシリアル キューを使用する場合の唯一の違いは、mainQueue の代わりにそのキューにメッセージを送ることです。

于 2012-08-14T16:06:55.503 に答える