26

「isOpen == YES」という述語を持つfetchedResultsControllerがあります。

closeCurrentClockSetを呼び出すときは、そのプロパティをNOに設定します。したがって、tableView には表示されなくなります。

何らかの理由で、これは起こっていません。

誰かがこれを理解するのを手伝ってくれますか?

-(void)closeCurrentClockSet
{

    NSPredicate * predicate = [NSPredicate predicateWithFormat:@"isOpen == YES"];

    NSArray *fetchedObjects =
        [self fetchRequestForEntity:@"ClockSet"
                      withPredicate:predicate
             inManagedObjectContext:[myAppDelegate managedObjectContext]];

    ClockSet *currentClockSet = (ClockSet *)fetchedObjects.lastObject;

    [currentClockSet setIsOpen:[NSNumber numberWithBool:NO]];

}

--

カスタムfetchRequestForEntity:withPredicate:inManagedObjectContextメソッドを呼び出すことにより、まったく同じアプローチを使用して、さらにいくつかのメソッドがあります。

これらのメソッドでは、プロパティを変更すると、tableView が正しく更新されます。しかし、これは上のもの ( closeCurrentClockSet ) ではありません! 理由がわかりません。

--

私の fetchedResultsController の実装は、Apple のドキュメントからのものです。

また、別の詳細。アプリをバックグラウンドに送信すると。それを閉じて再度開くと、tableView が更新されたように表示されます。

ここで、stackOverflow に関する以前の質問に従うために最善を尽くしました。運がない。私もこれを骨にNSLoggedしました。オブジェクトは正しく取得されています。それは正しいものです。isOpen プロパティは正しくNOに更新されています。しかし、何らかの理由で、私の fetchedResultsController は tableView を更新しません。

reloadData や performFetch の呼び出しなど、いくつかの「ハンマー」ソリューションを試しました。しかし、それはうまくいきませんでした。または、それらを使用する意味があります...

編集:それはうまくいきました.resultControllerでperformFetchの直後にreloadDataを呼び出しますが、reloadDataを使用すると解決策が打たれます。さらに、すべてのアニメーションを削除します。コントローラーで tableView を自動更新したい。

誰かがこれを理解するのを手伝ってくれますか?

どんな助けでも大歓迎です!

ありがとうございました、

ヌーノ

編集:

完全な実装。

fetchedResultsController はかなり標準的で簡単です。他のすべてはAppleのドキュメントからのものです

- (NSFetchedResultsController *)fetchedResultsController
{

    if (_fetchedResultsController) {
        return _fetchedResultsController;
    }

    NSManagedObjectContext * managedObjectContext = [myAppDelegate managedObjectContext];

    NSEntityDescription *entity  =
        [NSEntityDescription entityForName:@"ClockPair"
                    inManagedObjectContext:managedObjectContext];

    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
        [fetchRequest setEntity:entity];

    NSString *predicate = [NSString stringWithFormat: @"clockSet.isOpen == YES"];
        [fetchRequest setPredicate: [NSPredicate predicateWithFormat:predicate]];

    NSSortDescriptor *sortDescriptor1 =
        [[NSSortDescriptor alloc] initWithKey:@"clockIn" ascending:NO];

    NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor1, nil];

        [fetchRequest setSortDescriptors:sortDescriptors];
        [fetchRequest setFetchBatchSize:20];

    NSFetchedResultsController *theFetchedResultsController =
        [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
                                            managedObjectContext:managedObjectContext
                                              sectionNameKeyPath:nil
                                                       cacheName:@"Root"];


    _fetchedResultsController = theFetchedResultsController;
    _fetchedResultsController.delegate = self;

    return _fetchedResultsController;

}

--

Apple のドキュメントの定型コード:

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
    // The fetch controller is about to start sending change notifications, so prepare the table view for updates.
    [self.tableView beginUpdates];
}



- (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:UITableViewRowAnimationTop];

            break;

        case NSFetchedResultsChangeDelete:

            [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
                             withRowAnimation:UITableViewRowAnimationFade];

            break;

        case NSFetchedResultsChangeUpdate:

            [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
                             withRowAnimation:UITableViewRowAnimationFade];

            [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
                             withRowAnimation:UITableViewRowAnimationFade];

            break;

        case NSFetchedResultsChangeMove:

            [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
                             withRowAnimation:UITableViewRowAnimationLeft];

            [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
                             withRowAnimation:UITableViewRowAnimationTop];

            break;
    }
}



- (void)controller:(NSFetchedResultsController *)controller
  didChangeSection:(id )sectionInfo
           atIndex:(NSUInteger)sectionIndex
     forChangeType:(NSFetchedResultsChangeType)type
{

    UITableView *tableView = self.tableView;

    switch(type) {

        case NSFetchedResultsChangeInsert:

            [tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex]
                     withRowAnimation:UITableViewRowAnimationFade];

            break;

        case NSFetchedResultsChangeDelete:

            [tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex]
                     withRowAnimation:UITableViewRowAnimationFade];

            break;
    }
}



- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
    // The fetch controller has sent all current change notifications, so tell the table view to process all updates.
    [self.tableView endUpdates];
}

1回目の更新:

[managedObjectContext hasChanges]を追跡すると、当然のことながら YES が返されます。しかし、fetchedResultsController は tableView を更新しません

2回目の更新

didChangeObject:atIndexPath:は、この特定のケースでは呼び出されません! まったく同じコードを使用するメソッドがさらに 2 つありますが、それらはたまたま別のエンティティです。そして、それらは完璧に機能します。これを指摘してくれてありがとう@Leonardo

3回目の更新このメソッドは、同じルールに従います。しかし、実際には機能します。

- (void)clockOut
{
    NSPredicate * predicate = [NSPredicate predicateWithFormat:@"isOpen == %@", [NSNumber numberWithBool:YES]];

    NSArray * fetchedObjects =
        [self fetchRequestForEntity:@"ClockPair"
                      withPredicate:predicate
             inManagedObjectContext:[myAppDelegate managedObjectContext]];

    ClockPair *aClockPair = (ClockPair *)fetchedObjects.lastObject;

    aClockPair.clockOut = [NSDate date];
    aClockPair.isOpen   = [NSNumber numberWithBool:NO];


}

私が見逃している可能性があるものについて、他に何かアイデアはありますか?

ありがとうございました、

ヌーノ

4

3 に答える 3

79

OK、問題を説明してから、FRC のバグかどうかを判断させてください。バグだと思われる場合は、Apple にバグ レポートを提出する必要があります。

フェッチ結果コントローラーの述語は次のようになります。

NSString *predicate = [NSString stringWithFormat: @"clockSet.isOpen == YES"];

これは、ブール値の有効な述語です。clockSetエンティティの関係をたどり、そのisOpen属性を取得します。そうである場合YES、それらのオブジェクトはオブジェクトの配列に受け入れられます。

ここまでは順調だと思います。

ここで、clockSet.isOpen属性の 1 つを に変更するとNO、そのオブジェクトがテーブル ビューから消えることが予想されます (つまり、述語と一致しなくなるため、フェッチされたオブジェクトの配列から削除する必要があります)。

では、これがあれば…

[currentClockSet setIsOpen:[NSNumber numberWithBool:NO]];

currentClockSet次に、フェッチされた結果の FRC 配列から「消える」必要がある との関係を持つトップレベルのオブジェクト。

ただし、それが消えることはありません。その理由は、FRC によって監視されるオブジェクトが変更されなかったためです。はい、述語キー パスが変更されましたが、FRC はエンティティを保持ClockPairし、ClockSetエンティティは実際に変更されました。

通知が飛び交うのを見て、舞台裏で何が起こっているかを確認できます。

とにかく、FRC はフェッチを行うときにキー パスを使用しますが、フェッチされたオブジェクトの実際のセットにないオブジェクトへの変更は監視しません。

最も簡単な回避策は、このキー パス オブジェクトを保持するオブジェクトの属性を「設定」することです。

たとえば、 にClockPairisOpen属性があることに気付きました。あなたが逆の関係を持っているなら、あなたはこれを行うことができます...

currentClockSet.isOpen = NO;
currentClockSet.clockPair.isOpen = currentClockSet.clockPair.isOpen;

実際には値をまったく変更していないことに注意してください。ただし、セッターが呼び出され、KVO がトリガーされ、プライベートの DidChange 通知がトリガーされ、オブジェクトが変更されたことを FRC に伝えました。したがって、チェックを再評価して、オブジェクトを含める必要があるかどうかを確認し、キーパス値が変更されていることを確認して、期待どおりに動作します。

したがって、FRC 述語でキー パスを使用する場合、その値を変更する場合は、FRC 配列内のすべてのオブジェクトにワーム バックし、それらのオブジェクトが通知に含まれるように「ダーティ アップ」する必要があります。オブジェクトの変更について回されました。見苦しいですが、取得リクエストを保存または変更して再取得するよりはましかもしれません。

私はあなたが私を信じていないことを知っているので、試してみてください. これが機能するためには、オブジェクトの FRC 配列内のどのアイテムが変更の影響を受けるかを知り、FRC に変更を通知させるためにそれらを「突く」必要があることに注意してください。

もう 1 つのオプションは、前述したように、コンテキストを保存して値を再フェッチすることです。コンテキストを保存したくない場合は、ストアから更新せずに、フェッチに現在のコンテキストの更新を含めることができます。

FRC が監視しているオブジェクトへの変更を偽装することが、他のエンティティへのキー パスである述語の再評価を達成するための最良の方法であることがわかりました。

さて、これがバグかどうかは議論の余地があります。個人的には、FRC がキーパスを監視する場合は、ここで見たように部分的にではなく、完全に監視する必要があると思います。

バグレポートを提出することをお勧めします。

于 2012-09-12T00:44:13.657 に答える
5

同様の問題に遭遇しました。

私はこの質問がかなり古いことを知っていますが、これが他の誰かに役立つことを願っています:

lastUpdated: NSDate最も簡単な方法は、親オブジェクトで名前が付けられた新しいプロパティを導入することでした。

複数のConversationを含む がありましたMessages。メッセージのフラグが更新されるたびに、 sのみを表示isReadする を更新する必要がありました。さらに、はのみをフェッチし、については何も知りません。ConversationOverviewViewControllerConversationNSFetchedResultsControllerConversationOverviewVCConversationMessage

メッセージが更新されるたびに、私は に電話しmessage.parentConversation.lastUpdated = NSDate()ました。これは、更新を手動でトリガーする簡単で便利な方法です。

お役に立てれば。

于 2016-02-16T11:06:05.533 に答える
0

その後[currentClockSet setIsOpen:[NSNumber numberWithBool:NO]];、管理対象オブジェクトのコンテキストを保存できます:

NSError *saveError = nil;
if( ![[myAppDelegate managedObjectContext] save:&saveError] ) {
    // handle error saving context
}

UITableViewコンテキストを保存した後、適切に更新されると思います。これが、アプリをバックグラウンドに送信することが機能する理由である可能性があります。あなたの Core Data スタックは、アプリケーションのデリゲートでNSManagedObjectContext、バックグラウンドになったときにメインで保存を実行するように設定されていると思われます。

于 2012-09-11T13:36:09.290 に答える