2012-04-12 60 views
5

我在這樣定義上的NSManagedObjectContext一個GCD調度隊列做業務:NSSortDescriptor背景後工作不保存

- (NSManagedObjectContext *)backgroundContext 
{ 
    if (backgroundContext == nil) { 
     self.backgroundContext = [NSManagedObjectContext MR_contextThatNotifiesDefaultContextOnMainThread]; 
    } 
    return backgroundContext; 
} 

MR_contextThatNotifiesDefaultContextOnMainThreadMagicalRecord方法:

NSManagedObjectContext *context = [[self alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; 
[context setParentContext:[NSManagedObjectContext MR_defaultContext]]; 
return context; 

後提取我的對象,並給他們正確的隊列位置,我登錄他們,順序是正確的。然而,第二個日誌似乎是完全隨機的,排序描述符顯然不起作用。

我已將問題範圍縮小到[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); 
    } 

}); 

我有不知道爲什麼排序描述符不能正常工作,任何核心數據專家想幫幫忙?

更新:

不會發生此問題上的iOS 4,我想原因是在線程隔離和專用隊列模式之間的區別的地方。 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提供的方法以遞歸方式保存現在我的代碼看起來像這樣:

對我

恥辱首先不使用它...

不過,我不知道爲什麼幫助並希望得到一個解釋。

回答

2

我會盡力作出評論,因爲我寫MagicalRecord。

因此,在iOS5上,MagicalRecord設置爲嘗試使用多個託管對象上下文的新Private Queue方法。這意味着保存在子上下文中只會將保存到父項。只有沒有父母的父母才能保存,這種保存是否會繼續存在。這可能是你的MagicalRecord版本中發生的事情。

MagicalRecord試圖在以後的版本中爲你處理這個問題。也就是說,它會嘗試在私有隊列模式和線程隔離模式之間選擇。正如你發現的那樣,這並不好。對於iOS4和iOS5,唯一真正兼容的編寫代碼的方式(無需複雜的預處理器規則等)使用傳統的線程隔離模式。來自1.8.3標籤的MagicalRecord支持這一點,並且應該適用於兩者。從2.0開始,這將只是從這裏開始的私人隊列。

而且,如果你看看MR_save方法,你會發現它也在爲你執行hasChanges檢查(這也可能不需要,因爲核心數據內部也可以處理)。無論如何,你應該編寫和維護更少的代碼...

+1

感謝您的輸入,我只嘗試了更多的線程隔離,但我更願意在iOS 5上使用專用隊列。在測試期間,我認爲我發現了一個錯誤。父上下文的保存會在子上下文的線程中調用,這會在從GCD上下文遞歸保存時導致問題。我已提交合並請求:https://github.com/magicalpanda/MagicalRecord/pull/159 – tim 2012-04-13 12:00:05

-2

由於CoreData不是一個安全線程框架,並且對於每個線程(操作隊列),核心數據都使用不同的上下文。請參考以下優秀的寫作

http://www.duckrowing.com/2010/03/11/using-core-data-on-multiple-threads/

+0

backgroundContext從GCD塊初始化,沒有ManagedObject或ManagedObjectContext穿越線程邊界,所以我不認爲這是我的問題在這裏。 – tim 2012-04-12 10:15:45

1

爲什麼你的原始設置不起作用的實際潛在原因是當父級上下文尚未完成時從子類上下文中使用排序描述符獲取時的Apple bug保存到存儲:

NSSortdescriptor ineffective on fetch result from NSManagedContext

如果有什麼辦法可以規避嵌套的環境中,一定要避免他們,因爲他們仍然是非常馬車,你很可能會與所謂的性能提升失望,比照。此外: http://wbyoung.tumblr.com/post/27851725562/core-data-growing-pains