2010-03-19 94 views
2

我有一個與dueDate屬性的託管對象。除了使用一些醜陋的日期字符串作爲我的UITableView的節頭我創建了一個名爲「類別」一過性屬性,顯示的定義它像這樣:自定義部分名稱崩潰NSFetchedResultsController

- (NSString*)category 
{ 
    [self willAccessValueForKey:@"category"]; 

    NSString* categoryName; 
    if ([self isOverdue]) 
    { 
     categoryName = @"Overdue"; 
    } 
    else if ([self.finishedDate != nil]) 
    { 
     categoryName = @"Done"; 
    } 
    else 
    { 
     categoryName = @"In Progress"; 
    } 

    [self didAccessValueForKey:@"category"]; 
    return categoryName; 
} 

這裏是NSFetchedResultsController設置:

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init]; 
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Task" 
              inManagedObjectContext:managedObjectContext]; 
[fetchRequest setEntity:entity]; 

NSMutableArray* descriptors = [[NSMutableArray alloc] init]; 
NSSortDescriptor *dueDateDescriptor = [[NSSortDescriptor alloc] initWithKey:@"dueDate" 
                    ascending:YES]; 
[descriptors addObject:dueDateDescriptor]; 
[dueDateDescriptor release]; 
[fetchRequest setSortDescriptors:descriptors]; 

fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:@"category" cacheName:@"Root"]; 

表格最初顯示正常,顯示其dueDate未在標題爲「正在進行」的部分中傳遞的未完成項目。現在,用戶可以點擊表格視圖中的一行,將新的詳細信息視圖推送到導航堆棧上。在這個新視圖中,用戶可以點擊一個按鈕來指示該項目現在是「完成」。下面是該按鈕的處理程序(self.task是管理對象):

- (void)taskDoneButtonTapped 
{ 
    self.task.finishedDate = [NSDate date]; 
} 

只要我打這個異常的「finishedDate」屬性更改值:

2010-03-18 23:29:52.476 MyApp[1637:207] Serious application error. Exception was caught during Core Data change processing: no section named 'Done' found with userInfo (null) 
2010-03-18 23:29:52.477 MyApp[1637:207] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'no section named 'Done' found' 

我已經弄清楚,目前被新細節視圖隱藏的UITableView試圖更新它的行和部分,因爲NSFetchedResultsController被告知數據集中的某些內容發生了變化。這裏是我的表更新代碼(無論從核心數據配方樣品或樣品CoreBooks複製 - 我不記得是哪):

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller 
{ 
    [self.tableView beginUpdates]; 
} 

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath 
{ 
    switch(type) 
    { 
     case NSFetchedResultsChangeInsert: 
      [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade]; 
      break; 

     case NSFetchedResultsChangeDelete: 
      [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; 
      break; 

     case NSFetchedResultsChangeUpdate: 
      [self configureCell:[self.tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath]; 
      break; 

     case NSFetchedResultsChangeMove: 
      [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; 
      // Reloading the section inserts a new row and ensures that titles are updated appropriately. 
      [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:newIndexPath.section] withRowAnimation:UITableViewRowAnimationFade]; 
      break; 
    } 
} 

- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type 
{ 
    switch(type) 
    { 
     case NSFetchedResultsChangeInsert: 
      [self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade]; 
      break; 

     case NSFetchedResultsChangeDelete: 
      [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade]; 
      break; 
    } 
} 

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller 
{ 
    [self.tableView endUpdates]; 
} 

我把斷點在每個功能,發現只有controllerWillChange被稱爲。在兩個控制器之一之前拋出異常:didChangeObject:atIndexPath:forChangeType:newIndex或controller:didChangeSection:atIndex:forChangeType被調用。

在這一點上,我卡住了。如果我將我的sectionNameKeyPath更改爲「dueDate」,那麼一切正常。我認爲這是因爲dueDate屬性永遠不會改變,而在finishedDate屬性更改後回讀時類別會有所不同。

請幫忙!

UPDATE:

這裏是我的UITableViewDataSource代碼:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 
{ 
    return [[self.fetchedResultsController sections] count]; 
} 

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 
{ 
    id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section]; 
    return [sectionInfo numberOfObjects]; 
} 

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    static NSString *CellIdentifier = @"Cell"; 

    UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 
    if (cell == nil) 
    { 
     cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease]; 
    } 

    [self configureCell:cell atIndexPath:indexPath];  

    return cell; 
} 

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section 
{ 
    id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section];  
    return [sectionInfo name]; 
} 

回答

1

它看起來對我來說,你的問題在於你的「類別」短暫的屬性,您正在使用,以提供sectionNameKeyPath。 sectionNameKeyPath必須與主排序描述符相同。就你而言,這意味着所有「逾期」任務必須具有早於所有「完成」任務的日期必須早於所有「正在進行」任務的日期。可以構建一個場景,其中「完成」任務的「到期日期」任務之後有dueDate,或者出現在「逾期」任務之前。這個場景打破了sectionNameKeyPath的順序要求,並導致NSFetchedResultsController拋出一個NSInternalConsistencyException異常。

我建議你的問題的解決方案,不涉及滾動你自己的數組,然後必須拆分成部分。在模型中創建一個整數屬性,將0映射到「過期」,1映射到「完成」,將2映射到「正在進行」。將它作爲NSFetchRequest中的主要排序描述符並按升序對此屬性進行排序。向NSFetchRequest添加一個輔助排序描述符,以升序排序dueDate屬性。修改您的類別方法,從您在上面創建的整數屬性派生類別名稱,並將其用作您的sectionNameKeyPath。您將需要更新整數屬性以更新任務,因爲他們從正在進行的過渡到完成等。

+1

我很尊重馬庫斯 - 我不認爲這是一個錯誤。 NSFetchedResultsController的文檔聲明:「獲取請求必須至少有一個排序描述符,如果控制器生成段,數組中的第一個排序描述符用於將對象分組爲段;它的鍵必須與sectionNameKeyPath相同或使用其鍵的相對順序必須與使用sectionNameKeyPath的順序相匹配。「正如我所看到的,發生崩潰的原因是排序描述符的鍵和sectionNameKeyPath的鍵具有不同的排序順序。 – glorifiedHacker 2010-03-27 20:00:06

+0

非常有趣的建議。基於我從SDK文檔中讀取的內容具有意義。我已經走過了一個單獨陣列的路線,但我會看看我是否可以在某個時候嘗試一下。謝謝! – 2010-04-04 07:03:41

2

崩潰是由NSFetchedResultsController不知道「完成」類別之前,因此崩潰造成的。我在其他問題中幾次看到過這次崩潰,並且我建議每次都向蘋果公司提交雷達票。這是NSFetchedResultsController中的一個錯誤。

+0

只是偶然發現了這個問題,因爲我的應用程序在iOS 3下崩潰正是因爲這個原因,而不是iOS 4下。將不得不找到一個解決方法或下降3.x支持... – Pascal 2011-02-14 10:09:00