1

私はdispatch_queueを初めて使用し、バックグラウンドでCoreDataに保存しようとして問題に遭遇しました。CoreData プログラミング ガイドを読み、NSManagedObjectContextバックグラウンド スレッドで別のスレッドを作成しています。テスト プロジェクトで sを作成する単純なループを実行するとNSManagedObject、問題は発生しません。オブジェクトが作成され、 を使用しNSManageObjectContextDidSaveNotificationて変更をメイン スレッドに伝えます。

私の問題は、GCD を知らないことにあると思います。XML を解析していますparserDidEndDocument:が、UI をブロックせずにデータを CoreData に保存する必要があります。このブロックが使用されるたびに、アプリのメモリが手に負えないほど雪だるま式に増え始め、最終的Terminated app due to memory pressure

注: AppDelegate のシングルトンを使用して my を保持し、NSPersistentStoreCoordinatorstuffToSave はNSMutablearrayパーサーによって作成されます。

どんな方向性でも大歓迎です。私は2日間頭を殴っています!

-(void)parserDidEndDocument:(NSXMLParser *)parser

dispatch_queue_t backgroundQ = dispatch_queue_create("com.example.myapp", NULL);

__block AppDelegate *app= [[UIApplication sharedApplication]delegate];
__block NSMutableArray *array = self.stuffToSave;


dispatch_async(backgroundQ, ^(void){

    NSManagedObjectContext *context = [[NSManagedObjectContext alloc] init];
    context.persistentStoreCoordinator = [app persistentStoreCoordinator];

    HNField *field = [HNField fieldWithField_id:[NSNumber numberWithInt:0] inContext:context];
    //initalize array if needed
    if (!field.arrayOfPolylines) field.arrayOfPolylines = [[NSMutableArray alloc]init];

    //add polyline to array to save in database
    for (id obj in array) {
        if ([obj isKindOfClass:[HNPolyline class]]) {
            HNPolyline *theLine = (HNPolyline *)obj;
            [field.arrayOfPolylines addObject:theLine];
        }else if ([obj isKindOfClass:[HNParserPoint class]]){
            HNPoint *point = [HNPoint createAPointWithContext:context];
            HNParserPoint *pPoint = (HNParserPoint *)obj;
            point.point_id = pPoint.point_id;
            point.lat = pPoint.lat;
            point.lng = pPoint.lng;
            point.yield = pPoint.yield;
            point.farm_id = self.farm_id;
            point.field_id = self.field_id;
            point.inField = field;
            //add every point in database
            [field addFieldPointsObject:point];
        }
    }
    NSError *error;
    [context save:&error];

       });



self.stuffToSave = nil;
self.parser = nil;
}

編集 1:NSManageObjectContextDidSaveNotification解析を行っている場所とは異なるクラスから リッスンしています。私が持ってviewDidLoadいる:

// observe the ParseOperation's save operation with its managed object context
[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(didSave:)
                                             name:NSManagedObjectContextDidSaveNotification
                                           object:nil];

次に、Apple の「ThreadedCoreData」の例から以下を使用しています。

-(void)didSave:(NSNotification *)notification{

    if (notification.object != [self.app managedObjectContext]) {
        NSLog(@"not main context");
        [self performSelectorOnMainThread:@selector(updateMainContext:) withObject:notification waitUntilDone:NO];
    }else{
         NSLog(@"b Thread: %@",[NSThread currentThread]);
       NSLog(@"main context");
    }
}

// merge changes to main context
- (void)updateMainContext:(NSNotification *)notification {

    assert([NSThread isMainThread]);
    [[self.app managedObjectContext] mergeChangesFromContextDidSaveNotification:notification];
    NSLog(@"did save");
}
4

2 に答える 2

1

よく理解できていないのかもしれませんが、私が見ている限り、避けたいことはほとんどありません。

私は間違っているかもしれませんが、問題はバックグラウンド キューで非同期操作を起動していることだと思いますが、スレッド セーフであることが保証されているデフォルトのコンテキスト キューで作業することになりますが、そうではありません。したがって、保存しているときに、メモリがいっぱいになるまで、保存などをトリガーする通知を送信します。これを確認するには、updateMainContext にブレーク ポイントを設定して、それが何回呼び出されるかを確認します。

異なるコンテキストで作業している場合は、NSPrivateQueueConcurrencyType でそれらを初期化する必要があります。これにより、すべての作業が別のスレッドで確実に行われます。したがって、dispatch async の代わりにこのように再訪します。この場合、dispatch_async の代わりにこれを行うことができます。

NSManagedObjectContext *privateCtx = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; 

[privateCtx performBlock:^() {
  // put the code you have in dispatch async...
}];

次に、他のクラスで、プライベートコンテキストのみをリッスンする方法を見つける必要があります。もちろん、参照が必要です。できない場合は、そのクラスがバックグラウンドからのマージのポイントであることが意図されている場合は、メインスレッドから来るものをすべて回避しても問題ないと思います。

于 2013-12-18T16:29:46.923 に答える
0

私が Google Maps SDK (1.6.1.6332) を使用していること、および Google Maps SDK がコア データも使用していることを投稿したとき、言及も認識もしていませんでした。NSManagedObjectContextDidSaveNotificationアプリのデリゲートで観察していましたが、通知をフィルタリングできませんでした。したがって、Google マップは を送信しNSManagedObjectContextDidSaveNotification、アプリはそれらの変更をマージしようとしていました。これが問題の原因でした。

これが最善の解決策かどうかはわかりませんが、私の目的には適しています。アプリのデリゲートで通知をテストしているので、メインの MOC への参照しかないので、それを使用して、通知がそこからのものではないことを確認し、次に、通知が対象のクラスからのものかどうかをテストします。通知の文字列説明 (マージする必要がある対象のクラスの複数のインスタンスを含む)。そうすることで、Google マップがその変更をモデルにマージしようとするのをブロックします。最終的に答えにつながる彼の意見に対して@Leonardoに感謝します。

- (void)mergeChanges:(NSNotification *)notification {

    NSLog(@"saved changes called");
    if (notification.object != self.managedObjectContext) {
        NSLog(@"call was not main MOC");
        NSString *testingString = notification.description;
        if ([testingString rangeOfString:@"HNDemoResponse"].location != NSNotFound) {
            [self performSelectorOnMainThread:@selector(updateMainContext:) withObject:notification waitUntilDone:YES];
        }else NSLog(@"call was goole maps");
    }
}
于 2014-01-03T15:26:36.633 に答える