2014-04-12 35 views
0

我有一個非常令人沮喪的問題,我想幫助理解甚至修復。iOS核心數據「樂觀鎖定失敗」

我正在通過構建一個相當簡單的應用程序來學習Core Data。

我的模型如下:

用戶

屬性

  • 性別
  • DOB

關係

  • hasCompletedItem -

  • isInAgeGroup 「用戶可以完成許多列表項,列表項可以由許多用戶完成」 - 「用戶是一個AgeGroup,一AgeGroup可以包含多個用戶」

AgeGroup

屬性

  • 標題

關係

  • hasItems - 「一個AgeGroup有許多與其相關的listItems中,一個ListItem只能與一個AgeGroup相關「

  • hasUsers - 「一個AgeGroup具有與之相關的許多用戶,用戶可以僅在1個AgeGroup」

的ListItem

屬性

  • itemText

個關係

  • completedByUser - 「列表項可以由多個用戶完成,用戶可以完成許多列表項」。

  • forAgeGroup - 「列表項被分配到一個單一的AgeGroup,一個AgeGroup可以有多個listItems中的」

我的程序設置如下:

  • 的AppDelegate中處理創建CoreData堆棧。
  • didFinishLaunchingWithOptions訪問MenuViewController並穿過managedObjectContext爲它的使用方法:
id navigationController = [[self window] rootViewController]; 
id controller = [navigationController topViewController]; 
[controller setManagedObjectContext:[self managedObjectContext]]; 
  • 的MenuViewController顯示器。它是一個帶有幾個按鈕的UIViewController,每個按鈕都會延伸到其他視圖控制器。
  • 當按下上MenuViewController的 '開始' 按鈕,現有managedObjectContext在prepareForSegue方法傳遞:然後
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender 
{ 
    if ([segue.identifier isEqualToString:@"selectprofile"]) { 
     [[segue destinationViewController] setManagedObjectContext:[self managedObjectContext]]; 
    } 
} 
  • 的SelectProfileTableViewController被示出。它列出每個用戶的表格。它有一個允許添加新用戶的按鈕。 tableview的數據由NSFetchedResultsController提供,它基本上只提取用戶實體中的所有記錄。
  • 攻 「添加」 按鈕,將加載AddProfileViewController而不是通過對managedObjectContext前:
AddProfileViewController *viewController = (AddProfileViewController *)[[segue destinationViewController] topViewController]; 
[viewController setManagedObjectContext:self.managedObjectContext]; 
  • 的AddProfileViewController僅僅是一個UIViewController。它具有名稱的文本字段,D.O.B等。
  • 它有一個方法addProfileDone,當用戶點擊「完成」按鈕時被調用。在該方法中,創建新的用戶managedObject及其屬性被設置:
User *newMO = [NSEntityDescription insertNewObjectForEntityForName:@"User" inManagedObjectContext:_managedObjectContext]; 

newMO.name = self.nameTextField.text; 
newMO.dob = _dob; 
// etc 
  • 接下來,它也試圖設置這個新的用戶實體之間的關係,它的相應的AgeGroup實體。
NSFetchRequest *ageGroupRecordRequest = [NSFetchRequest fetchRequestWithEntityName:@"AgeGroup"]; 

NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"title" 
                 ascending:YES]; 
[ageGroupRecordRequest setSortDescriptors:[NSArray arrayWithObject:sort]]; 

// Make a predicate to find the correct AgeGroup based on what was calculated 
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"(title == %@)", ageGroup]; 
[ageGroupRecordRequest setPredicate:predicate]; 

// Run the fetch request, should only ever get 1 result back. 
NSError *fetchError; 

// Result will be an array with a single AgeGroup entity 
NSArray *fetchedObjects = [_managedObjectContext executeFetchRequest:ageGroupRecordRequest error:&fetchError]; 


// Set up the relationship using the fetched entity 
// Crashes when saving if below line is uncommented 
newMO.isInAgeGroup = fetchedObjects[0]; 
  • 如上代碼注意到,當行則取消註釋和managedObjectContext去救人,我得到以下錯誤:

    CoreData:錯誤:未能解決持樂觀態度鎖定失敗:樂觀鎖定失敗(null) CoreData:錯誤:無法解決樂觀鎖定失敗。舊的保存請求是:{inserts(( 「0x942dd40」 )),updates(( 「0x8e4ed60」 )),deletes()locks()} 2014-04-12 20:00:09。482 ChekList [6076:60b] CoreData:錯誤:未能解決樂觀鎖定失敗。下一次嘗試將是:{插入物(( 「0x942dd40」 )),更新(( 「0x8e4ed60」 )),刪除()鎖()} SQL:BEGIN EXCLUSIVE 註釋:獲得最大峯爲ENTITYID = 3 註釋:使用old = 4017和new = 4018更新entityID = 3的最大pk值sql​​:COMMIT sql:BEGIN EXCLUSIVE sql:UPDATE ZAGEGROUP SET Z_OPT =?哪裏Z_PK =? AND(Z_OPT =?OR Z_OPT IS NULL) details:SQLite bind [0] =(int64)1 詳細信息:SQLite bind [1] =(int64)6 詳細信息:SQLite bind [2] = nil sql:ROLLBACK SQL:SELECT Z_PK,Z_OPT FROM ZAGEGROUP WHERE Z_PK(6)ORDER BY Z_PK 註釋:SQL執行時間:0.0006s

我的理解是,基本上別的東西已經修改了背景,並且在保存CoreData已經注意到這一點,並停止。我已閱讀關於更改ManagedObjectContext上的MergePolicy,但我不想知道爲什麼必須這樣做。

有趣的是,如果我註釋掉試圖設置關係的線條,它可以正常工作。 (當然關係沒有設置)

據我可以看到,我正確地將managedObjectContext傳遞給每個視圖控制器。我還確保沒有多個上下文訪問相同的持久性存儲。

是否有可能是之前由於某種原因正在修改上下文的View Controller中的FetchedResultsController?

是否有人能夠提供一些信息和可能的解決方案?我寧願不必更改合併策略。我看不出爲什麼我不得不考慮它的一個相當簡單的例子。在這一天的大部分時間裏,我一直在拉我的頭髮。

我不禁想到這很可能是我錯過的簡單東西。

謝謝, Brett。

回答

2

我能弄清楚解決方案。

原來我的整個問題都是由我預先加載的數據庫造成的。 我有一個預加載的數據庫,我在AppDelegate中複製。結果發現有些問題。

我通過註釋掉複製數據庫的行而發現了這一點,而是在AppDelegate中手動添加了預加載的數據。我能夠添加數據並設置關係。

從那裏我創建了一個新的預加載數據庫,它工作正常。

我還發現了一個巨大的蘋果開發者的例子 -

https://developer.apple.com/library/ios/samplecode/iPhoneCoreDataRecipes/Introduction/Intro.html

這也表明我添加新實體的另一種方式。在這種情況下,他們在prepareForSegue方法中創建實際實體,並僅傳遞實體而不傳遞整個上下文。

我改變了我的應用程序以使用該方法(在我發現數據庫是問題之前),但它仍然崩潰。我決定保持這種狀態,因爲它看起來更優雅。

0

我有這個相同的問題,但建議的解決方案不適合我。有效的是使用併發類型創建上下文:NSMainQueueConcurrencyType即使我不需要它,並且根本沒有使用併發性,但以某種方式複製預加載的數據庫創建了樂觀鎖定失敗。然後包裹保存在調用上下文的performBlockAndWait:

要創建上下文:

[[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType]; 

要保存更改:

__block NSError *error = nil; 
__block BOOL savedOK = NO; 
[managedObjectContext performBlockAndWait:^{ 
    savedOK = [managedObjectContext save:&error]; 
}]; 

這在iOS的API甚至記錄,尋找ConcurrencyNSManagedObjectContext類。

+0

發現這不是一個解決方案。問題在於,在將對象添加到該表的其他非反身關係之前,需要保存我的反身關係。 –