2011-02-01 17 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]); 
} 

問題は、それがダウンロードされ、Webサービスから同期として最初に店が何の実体を持たないということです。 NSFetchedResultsControllerは、ストアからの150行以上のエンティティをテーブルに埋め込みます。これは、Webサービスから返されるエンティティの数です。しかし、私はそれが無視されているように見える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を持っているかどうかに基づいて解決策を提供します。私(あなたのような)はしなかったので、答えはfetchedResultsControllerからテーブルビューを切り離すことです。たとえば、代わりの行数を決定するためにそれを使用して:

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

はちょうどあなたが期待するものを返す:newIndexPathはあなたのfetchLimit内であれば

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

そしてcontroller:didChangeObjectでは、唯一の新しいオブジェクトを挿入。

+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でこれが打たれました。私は実際にAppleがこれをまだ解決していないことを理解していない。バグレポートの時間は、 –

+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

私はこの問題についてのiOS 6/7に戻って2014年にAppleとバグレポートを提出しました。他の多くの人が指摘しているように、それはまだ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で)関数をトリガします。あなたがもうそれを必要としない場合、そのオブザーバーを削除することを忘れないでください。