2011-02-01 2 views
21

코어 데이터의 내용으로 UITableView를 업데이트하는 NSFetchedResultsController가 있습니다. 그것은 꽤 표준적인 물건들입니다. 그러나 나는 당신이 모두 여러 번 보았을 것이라고 확신합니다. 그러나 나는 약간의 문제에 직면하고 있습니다. 첫 번째 코드는 다음과 같습니다.NSFetchedResultsController는 fetchLimit를 무시합니까?

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init]; 

NSEntityDescription *entity = [NSEntityDescription entityForName:@"Article" inManagedObjectContext:self.managedObjectContext]; 

[fetchRequest setEntity:entity]; 

[fetchRequest setFetchLimit:20]; 

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"(folder.hidden == NO)"]; 
[fetchRequest setPredicate:predicate]; 

NSSortDescriptor *sort1 = [NSSortDescriptor sortDescriptorWithKey:@"sortDate" ascending:NO]; 
[fetchRequest setSortDescriptors:[NSArray arrayWithObjects:sort1, nil]]; 

NSFetchedResultsController *controller = [[NSFetchedResultsController alloc] 
     initWithFetchRequest:fetchRequest 
     managedObjectContext:self.managedObjectContext 
     sectionNameKeyPath:nil 
     cacheName:nil]; 
[fetchRequest release]; 

controller.delegate = self; 

self.fetchedResultsController = controller; 

[controller release]; 

NSError *error = nil; 
[self.fetchedResultsController performFetch:&error]; 
if (error) { 
    // TODO send error notification 
    NSLog(@"%@", [error localizedDescription]); 
} 

문제는 처음에는 웹 서비스에서 다운로드하고 동기화 할 때 엔티티가 없습니다. NSFetchedResultsController는 테이블에서 150 개가 넘는 엔티티 행 (웹 서비스가 반환하는 항목 수)을 채 웁니다. 그러나 나는 그것을 무시하고있는 것처럼 보이는 20의 반입 제한을 설정 중입니다. 그러나 앱을 닫고 이미 상점에있는 데이터로 다시 시작하면 정상적으로 작동합니다. 내 대리자를 임 내가 이렇게 : 어떤 아이디어가 무엇

애플의 dev에 문서에서 복사 - 붙여 넣기 꽤 많이
#pragma mark - 
#pragma mark NSFetchedResultsControllerDelegate methods 

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


- (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)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject 
atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath { 

UITableView *tableView = self.tableView; 

switch(type) { 

    case NSFetchedResultsChangeInsert: 
     [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade]; 
     break; 

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

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

    case NSFetchedResultsChangeMove: 
     [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; 
     [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade]; 
     break; 
    } 
} 


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

을에 간다?

답변

1

당신이 가진 문제는 당신이 fetchedResultsController

- (void)viewDidLoad { 
    [super viewDidLoad]; 

    // Loading Articles to CoreData 
    [self loadArticle]; 
} 

- (void)ArticleDidLoadSuccessfully:(NSNotification *)notification { 
    NSError *error; 
    if (![[self fetchedResultsController] performFetch:&error]) { 
     // Update to handle the error appropriately. 
     NSLog(@"Unresolved error %@, %@", error, [error userInfo]); 
     abort(); // Fail 
    } 
    [tableView reloadData]; 
} 
+0

좋은 소리 나는 그에게 총을 내고 무슨 일이 일어나는 지 알려줄 것입니다. – marchinram

+0

나는 이것이 필수적이라고 확신하지 않는다 ... 그것이 효과가 있었느냐? –

+0

사람들은 왜 아직도 abort()를했는지 궁금합니다. 코드에서. 응용 프로그램이 거부됩니다 ;-) 적절한 오류 처리만으로도 충분합니다 (또는 NSAssert 만 사용하십시오) – Lukasz

5

모든 정보를로드하면됩니다 다음 호출 할 필요가 모든 것을 보여줍니다 그래서로드 fetchedResultsController가 전체 데이터를 충전하기 전에 호출하는 것입니다 이것은 낡은 질문이지만 나는 (iOS 5에서) 나 자신을 만났다. 나는 당신이 여기에 설명 된 버그를 겪고 있다고 생각한다 : https://devforums.apple.com/message/279576#279576.

해당 스레드는 sectionNameKeyPath를 가지고 있는지 여부에 따라 솔루션을 제공합니다. 나 (당신처럼)는 그렇지 않았기 때문에, 답은 fetviewResultsController에서 tableview를 분리하는 것이다. newIndexPath이 fetchLimit 내에있는 경우

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 
{ 
     return fetchLimit; 

그리고 controller:didChangeObject에 만 새 개체를 삽입을 :

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 
{ 
     return [[[self.fetchedResultsController sections] objectAtIndex:0] numberOfObjects]; 

당신이 무엇을 기대 반환 예를 들어, 대신을 사용하는 행의 수를 결정하는 .

+5

이 버그는 iOS 6 또는 7에서 아직 수정되지 않았습니다. – bugloaf

+4

iOS 8에 여전히 존재하는지 확인할 수 있습니다. – Devfly

+4

분명히 버그가 iOS 9도 마찬가지입니다. – spassas

11

나는이 오래된 질문 알아요,하지만 난 그것에 대한 해결책이 다음 NSFetchRequestfetchlimit을 존중하지 않는 NSFetchedResultsController에서 알려진 버그가 있으므로

를 수동으로 제한을 처리해야 UITableViewDataSourceNSFetchedResultsControllerDelegate 개의 레코드 중에서

있는 tableView : numberOfRowsInSection :

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 

    id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section]; 

    NSInteger numRows = [sectionInfo numberOfObjects]; 

    if (numRows > self.fetchedResultsController.fetchRequest.fetchLimit) { 

     numRows = self.fetchedResultsController.fetchRequest.fetchLimit; 
    } 

    return numRows; 
} 

컨트롤러 : didChangeObject : atIndexPath : forChangeType : newIndexPath :

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath { 

    switch(type) { 

     case NSFetchedResultsChangeInsert: 

      if ([self.tableView numberOfRowsInSection:0] == self.fetchedResultsController.fetchRequest.fetchLimit) { 
       //Determining which row to delete depends on your sort descriptors 
       [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:self.fetchedResultsController.fetchRequest.fetchLimit - 1 inSection:0]] withRowAnimation:UITableViewRowAnimationFade]; 

      } 

      [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] 
          withRowAnimation:UITableViewRowAnimationFade]; 
     break; 
     ... 
    } 
} 
+1

최근에 iOS 7에서이 문제가 발생했습니다. 이것이 가장 좋은 해결책이라고 생각합니다. 버그 리포트를 할 시간은 –

+3

위의 코드로이 버그를 해결할 수없는 것 같습니다. 여전히 예외와 다음 콘솔 로그가 표시됩니다. " CoreData : error : 심각한 응용 프로그램 오류입니다. -controllerDidChangeContent : 호출 시도 중 NSFetchedResultsController 대리자에서 예외가 발생했습니다. sert 행 50을 섹션 0에 추가하지만 userInfo (null)로 업데이트 한 후 섹션 0에는 50 행만 있습니다. " –

2

이 여전히 여러 삽입, 또는 움직임, 어떤 상황에서 충돌합니다 한도 초과,...

NSUInteger limit = controller.fetchRequest.fetchLimit; 
NSUInteger current = <current section objects count>; 
NSMutableArray *inserts = [NSMutableArray array]; 
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"row < %d", limit]; 

if (insertedIndexPaths.count) { 
    NSUInteger deletedCount = 0; 
    for (NSIndexPath *indexPath in insertedIndexPaths) { 
     if (indexPath.row >= limit) continue; 
      current++; 
      if (current > limit) { 
       deletedCount++; 
       current--; 
       [deletedIndexPaths addObject:[NSIndexPath indexPathForRow:limit - deletedCount inSection:indexPath.section]]; 
      } 
      [inserts addObject:indexPath]; 
    } 
} 
if (movedIndexPaths.count) { 
    for (NSIndexPath *indexPath in movedIndexPaths) { 
     if (indexPath.row >= limit) { 
      [updatedIndexPaths addObject:[NSIndexPath indexPathForRow:limit - 1 inSection:indexPath.section]]; 
     } else { 
      [inserts addObject:indexPath]; 
     } 
} 
} 
[updatedIndexPaths minusSet:deletedIndexPaths]; 
[deletedIndexPaths filterUsingPredicate:predicate]; 
[updatedIndexPaths filterUsingPredicate:predicate]; 
[_tableView insertRowsAtIndexPaths:inserts withRowAnimation:UITableViewRowAnimationFade]; 
[_tableView reloadRowsAtIndexPaths:[updatedIndexPaths allObjects] withRowAnimation:UITableViewRowAnimationNone]; 
[_tableView deleteRowsAtIndexPaths:[deletedIndexPaths allObjects] withRowAnimation:UITableViewRowAnimationFade]; 

[_tableView endUpdates]; 
deletedIndexPaths = nil; 
insertedIndexPaths = nil; 
updatedIndexPaths = nil; 
1

: 당신은 4 개 세트에 모든 변경 사항을 저장하고, 다른 4 개 배열을 계산하고/업데이트/삭제 (하나의 섹션이 있다고 가정) -[UITableView endUpdates]

같은 일부 일 전에있는 tableView에 삽입해야 사과 문서에서 : https://developer.apple.com/reference/coredata/nsfetchrequest/1506622-fetchlimit

If you set a fetch limit, the framework makes a best effort, but does not guarantee, to improve efficiency. For every object store except the SQL store, a fetch request executed with a fetch limit in effect simply performs an unlimited fetch and throws away the unasked for rows.

1

나는이 문제에 대해 아이폰 OS 6/7에 2014 년에 다시 애플과 버그 리포트를 제출했다. 많은 사람들이 지적했듯이, 여전히 iOS 9 및 10의 버그입니다. Apple의 의견이 없어도 본래의 버그 보고서는 여전히 열려 있습니다. Here is an OpenRadar copy of that bug report.

다음은 성공과 함께 사용 된 수정 사항이지만 여러 번 호출됩니다. 주의해서 사용하십시오.

내가 호출되는 NSManagedObjectContext 인스턴스에 '저장'방법 후 NSFetchedResultsController의 대리자를 설정합니다

@objc func controllerDidChangeContent(controller: NSFetchedResultsController) { 
    tableView.endUpdates() // Only needed if you're calling tableView.beginUpdates() in controllerWillChangeContent. 

    if controller.fetchRequest.fetchLimit > 0 && controller.fetchRequest.fetchLimit < controller.fetchedObjects?.count { 
      controller.performFetch() 
      // Reload the table view section here 
     } 
    } 
} 
0

이 내 트릭입니다.

  1. UIViewController에서 관찰자를 이름과 함께 설정하십시오. 예 : '동기화'가
  2. 사용자의 컨텍스트를 저장 한 후, 해당 이름을 가진 알림 게시 : 대리인

PS를 설정하는 '동기화'와 (당신의 ViewController에서) 함수를 트리거합니다. 더 이상 필요가 없다면 그 관찰자를 제거하는 것을 잊지 마십시오.

관련 문제