0

フェッチ要求を実行し、4 つのプロパティを更新するスレッドに 2 番目の MOC があります。メイン スレッドでは、2 番目の MOC からの変更をマージする NSManagedObjectDidSaveNotification ハンドラを登録しています。このメソッドにブレーク ポイントを設定し、更新されたオブジェクトをチェックすると、新しいプロパティが表示されます。ただし、配列にオブジェクト ID を追加し、ID を使用して同じオブジェクトにアクセスしてマージ後保存すると、古いプロパティが取得されます。

驚いたことに、2 つのうち 1 つのプロパティには新しい値があり、もう 1 つのプロパティにはないことがわかりました。これは気が遠くなるようなものであり、数時間のデバッグの後、この背後にある理由を理解できません。天才がこの問題に光を当ててくれると助かります。

アップデート

私のメインスレッドで:

[[NSNotificationCenter defaultCenter] addObserver:self
                                        selector:@selector(contextDidSave:)
                                                 name:NSManagedObjectContextDidSaveNotification
                                               object:nil];


- (NSManagedObjectContext *)mainThreadObjectContext 
{

    if (myMainThreadObjectContext == nil) {
       NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
    if (coordinator != nil) {
        myMainThreadObjectContext = [[NSManagedObjectContext alloc] init];
        [myStateManagedObjectContext setPersistentStoreCoordinator:coordinator];
        [myStateManagedObjectContext setMergePolicy:NSMergeByPropertyStoreTrumpMergePolicy];
        [myStateManagedObjectContext setRetainsRegisteredObjects:NO];
        [myStateManagedObjectContext setUndoManager:nil];
    }
}
return myMainThreadObjectContext;

}

このメソッドで、スレッド化された MOC で行われた変更をマージします

- (void) contextDidSave:(NSNotification *)notification
  {
if (![NSThread isMainThread])
{
    [self performSelectorOnMainThread:@selector(contextDidSave:) withObject:notification waitUntilDone:NO];
    return;
}
NSManagedObjectContext *moc = [notification object];
NSMutableSet *updatedObjects = [NSMutableSet set];

if (![moc isEqual:self.mainThreadManagedObjectContext] && [[moc persistentStoreCoordinator] isEqual:self.persistentStoreCoordinator])
{
    @synchronized(self)
    {

            NSDictionary *userinfoDict = [notification userInfo];
            NSSet *insertedObjectSet = [userinfoDict valueForKeyPath:@"inserted.objectID"];
            NSSet *updatedObjectSet =  [userinfoDict valueForKeyPath:@"updated.objectID"];
            [updatedObjects addObjectsFromArray:insertedObjectSet.allObjects];
            [updatedObjects addObjectsFromArray:updatedObjectSet.allObjects];



        @try {
            [self.mainThreadManagedObjectContext mergeChangesFromContextDidSaveNotification:notification];
        }
        @catch (NSException * e) {
            NSLog(@"Exception %@ when mergning contexts in -[%@ %@]", e, NSStringFromClass(self.class), NSStringFromSelector(_cmd));
        }


        if(updatedObjects.count>0)
        {
            [self performSelector:@selector(updatedStatus:) withObject:updatedObjects afterDelay:0.5];
        }

        NSError *saveError = nil;
        if (![self.mainThreadManagedObjectContext save:&saveError])
        {
            NSLog(@"Could not save objects in -[%@ %@]. Error = %@", NSStringFromClass([self class]), NSStringFromSelector(_cmd), saveError);
        }

       }


  }
  }

メイン スレッドには、MOC から既存のオブジェクトをフェッチし、サーバーから受信したプロパティを更新するメソッドがあります。ただし、セカンダリ スレッドには、新しいマネージド オブジェクトを挿入し、後でメイン スレッドでの処理に時間がかかるプロパティを更新するメソッドがあります。

2番目のスレッドには、次のコードがあります

-(void) startTaskThread
{
    thread_ = [[NSThread alloc] initWithTarget:self selector:@selector(executeTask)     
    object:nil];
    [thread_ start];
}


-(void) executeTask
{
  @synchronized(self)
  {
    [self saveContext];

     NSLog(@"\n\nStarting Task thread [%@]!!!!\n\n", self);

    for(;;)
    {
        if([[NSThread currentThread] isCancelled])
            break;

        @autoreleasepool
        {
            [condition_ lock];

            if(taskArray_.count==0)
            {
                [condition_ wait];
            }

            [condition_ unlock];

            @try {
                if(taskArray_.count>0)
                {
                    NSInvocation *invocation = [self.taskArray objectAtIndex:0];


                    if([[NSThread currentThread] isCancelled])
                        break;
                    else if(!([[NSThread currentThread] isCancelled]) && [self     
                 respondsToSelector:invocation.selector ])
                    {

                        [invocation invokeWithTarget:self];
                        [self.taskArray removeObjectAtIndex:0];
                    }

                }
            }
            @catch (NSException *exception) {
                NSLog(@"Exception %@", exception.debugDescription);
            }
            @finally {

            }
        }
    }

  }
    // Save Moc.
    if([[[self managedObjectContext] insertedObjects] count]>0 || ([[[self 
             managedObjectContext] updatedObjects] count]>0) )
    {

        NSError *error = nil;
        if(![[self managedObjectContext] save:&error])
        {
            NSLog(@"Moc Save Error %@", [error localizedDescription]);
        }

    }

     [self.managedObjectContext reset];

}  

セカンダリ スレッドの saveContext メソッドは次のとおりです。

-(void) saveContext
{

if(!thread_ || [thread_ isCancelled])
{
    // Clean up cache here  //
    return;
}


_/* --- Here i have code that iterates through the properties that were received from the network and stored in a cache of NSDictionary data type */_ 


// Now i save the threaded MOC.

if([[[self managedObjectContext] insertedObjects] count]>0 || ([[[self managedObjectContext] updatedObjects] count]>0) )
{

    NSError *error = nil;
    NSLog(@"We are merging the changes now.. [%d], [%d]", [[[self managedObjectContext] insertedObjects] count], [[[self managedObjectContext] updatedObjects] count]);
    if(![[self managedObjectContext] save:&error])
    {
        NSLog(@"Moc Save Error %@", [error localizedDescription]);
    }

}

    _// I create a dispatch queue here to repeteadly call this method. This serves as a hear-beat dispatch queue that checks for updated/inserted objects every 5 seconds._

    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
    dispatch_queue_t queue = dispatch_queue_create("com.myCompany.mocSaveQ", 0);
    dispatch_after(popTime, queue, ^(void)
    {
                       // Save Moc.
                       NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:@selector(saveContext)]];
                       invocation.selector = @selector(saveContext);
                       invocation.target = self;

                       [self addTask:invocation];

    });


    dispatch_release(queue);

}

ここで、デバッガーで、スレッド化された/2番目の MOC で [NSManagedObjectContext save] を呼び出す前に、スレッドで更新されたオブジェクトを出力しようとしましたが、その管理オブジェクトのそれぞれのプロパティに適用された変更が表示されます。ただし、メイン スレッドの contextDidSave: メソッドで更新されたオブジェクトの通知オブジェクトの userInfo ディクショナリを出力しようとすると、同じオブジェクトがリストされますが、スレッドで更新された 1 つのプロパティだけに新しい値がありません。これは私にとってかなり混乱しています。

更新 2

これをよりよく理解するのに役立つコードを次に示します。

私のメインスレッドで

データ ディクショナリは、プロパティとサブディクショナリを含む NSDictionary です。

-(void) processData:(NSDictionary*) data
 {
      for(NSString *key in data)
      {
        NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
        fetchRequest.entity = [NSEntityDescription entityForName:@"MyEntity" inManagedObjectContext:self.managedObjectContext];
    fetchRequest.predicate = [NSPredicate predicateWithFormat:@"lookupKey = %@", key];
    NSArray *fetchResults = nil;
    NSError *error = nil;

    @synchronized(@"fetch_request_for_lookupKey")
    {
        @try
        {
            fetchResults = [moc executeFetchRequest:fetchRequest error:&error];
        }
        @catch (NSException *exception)
        {
            NSLog(@"Exception %@ when executing fetch %@", exception, NSStringFromSelector(_cmd));
        }
        @finally
        {
            [fetchRequest release];
        }
    }


            NSManagedObject* myManagedObject = fetchResults.lastObject;
            // We get the dictionary here
            NSDictionary *dictionary = [taskDictionary objectForKey:lookUpKey];

            for(NSString *subKey in dictionary)
            {

                 if([key isEqualToString:@"name"])
                 {
                        myManagedObject.name = [dictionary objectForKey:subKey];
                 }
                 else if([key isEqualToString:@"phone"]
                 {   //....Update the value...//      }


            }
      }
 }

セカンダリ スレッド クラスでは、セカンダリ スレッドの saveContext メソッドで上記のデータ処理ロジックを使用しています。上記の場合、メイン スレッドの MOC を保存してから、スレッドで NSFetchRequest を実行します。また、保存を実行した後、セカンダリ スレッドで NSFetchRequest を実行する前に、メイン MOC に更新されたオブジェクトがないことを確認しました。

4

1 に答える 1

0

それは私がドキュメントで見つけたものです:

並行性

Core Dataは、スレッド(またはシリアル化されたキュー)の制限を使用して、管理対象オブジェクトと管理対象オブジェクトのコンテキストを保護します(「CoreDataとの同時実行」を参照)。この結果、コンテキストは、デフォルトの所有者がそれを割り当てたスレッドまたはキューであると想定します。これは、initメソッドを呼び出すスレッドによって決定されます。したがって、あるスレッドでコンテキストを初期化してから、それを別のスレッドに渡すことはできません。代わりに、永続ストアコーディネーターへの参照を渡し、受信スレッド/キューにそれから派生した新しいコンテキストを作成させる必要があります。NSOperationを使用する場合は、main(シリアルキューの場合)またはstart(コンカレントキューの場合)でコンテキストを作成する必要があります。

OS Xv10.7以降およびiOSv5.0以降では、コンテキストを作成するときに、それを使用する同時実行パターンを指定できます(initWithConcurrencyType:を参照)。

したがって、MOCオブジェクトを取得するには、このメソッドを使用することをお勧めします。

- (id)initWithConcurrencyType:(NSManagedObjectContextConcurrencyType)ct;
于 2013-01-09T01:11:27.537 に答える