簡體   English   中英

NSFetchedResultsController不更新?

[英]NSFetchedResultsController not updating?

當我的用戶看到圖片時,可以通過按下按鈕來喜歡它。 運行以下代碼:

- (void)feedback:(Item *)item isLiked:(bool)liked {
    // Update the item with the new score asynchornously
    NSManagedObjectID *itemId = item.objectID;
    dispatch_async(dispatch_get_main_queue(), ^{
        // Create a new managed object context and set its persistent store coordinator
        // Note that this **must** be done here because this context belongs to another thread
        AppDelegate *theDelegate = [[UIApplication sharedApplication] delegate];
        NSManagedObjectContext *localContext = [[NSManagedObjectContext alloc] init];
        [localContext setPersistentStoreCoordinator:[theDelegate persistentStoreCoordinator]];

        Item *localItem = (Item *)[localContext objectWithID:itemId];
        localItem.liked = [NSNumber numberWithBool:liked];
        localItem.updated_at = [NSDate date];
        NSError *error;
        if (![localContext save:&error]) {
            NSLog(@"Error saving: %@", [error localizedDescription]);
        }
    });

在我的應用中,LikedViewController顯示用戶喜歡的圖像。 LikedVC由連接到NSFetchedResultsController的UITableViewController組成。

LikedVC:

- (void)viewDidLoad
{
    [super viewDidLoad];

    // NSFetchedResultsController
    NSManagedObjectContext *moc = [(AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
    _fetchedResultsController = \
    [[NSFetchedResultsController alloc] initWithFetchRequest:[self.delegate getFetchRequest]
                                        managedObjectContext:moc
                                          sectionNameKeyPath:nil
                                                   cacheName:nil];  // TODO investigate whether we should bother with cache
    _fetchedResultsController.delegate = self;

    NSError *error;
    if (![[self fetchedResultsController] performFetch:&error]) {
        // Update to handle the error appropriately.
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        exit(-1);  // Fail
    }

    // Bottom loading bar
    self.tableView.tableFooterView = self.footerView;
    self.footerActivityIndicator.hidesWhenStopped = true;

    // ActivityIndicator
    self.activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
    self.activityIndicator.color = [UIColor blackColor];
    [self.tableView addSubview:self.activityIndicator];
    self.activityIndicator.hidesWhenStopped = true;
    // FIXME Unable to center it inside the tableView properly
    self.activityIndicator.center = CGPointMake(self.tableView.center.x, self.tableView.center.y - self.tabBarController.tabBar.frame.size.height);
}

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    // Automatically fetch when there is nothing in the UITableView
    if ([self tableView:self.tableView numberOfRowsInSection:0] == 0) {
        if ([self canFetch]) {
            [self refill];
        }
    }
}

- (void)viewDidDisappear:(BOOL)animated {
    [super viewDidDisappear:animated];

    if (self.operation && self.operation.isExecuting) {
        NSLog(@"Cancelling Operation: %@", self.operation);
        [self.operation cancel];
        self.isFetching = false;
    }
}

#pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    static NSString *CellIdentifier = @"FeedCell";
    Item *item = [_fetchedResultsController objectAtIndexPath:indexPath];

    FeedCell *cell = (FeedCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
    cell.item = item;
    cell.tag = indexPath.row;
    cell.customImageView.userInteractionEnabled = YES;

    // NOTE Don't try to do this at the UITableViewCell level since the tap will be eaten by the UITableView/ScrollView
    if (self.likeOnTap) {
        UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapped:)];
        tap.numberOfTapsRequired = 1;
        [cell.customImageView addGestureRecognizer:tap];
    }

    // Set up the buttons
    [cell.likeButton addTarget:self action:@selector(liked:) forControlEvents:UIControlEventTouchUpInside];
    [cell.dislikeButton addTarget:self action:@selector(disliked:) forControlEvents:UIControlEventTouchUpInside];
    [cell.detailButton addTarget:self action:@selector(detailed:) forControlEvents:UIControlEventTouchUpInside];

    [[SDImageCache sharedImageCache] queryDiskCacheForKey:item.image_url done:^(UIImage *image, SDImageCacheType type) {
        if (image) {
            [cell setCustomImage:image];
        } else {
            // If we have to download, make sure user is on the image for more than 0.25s before we
            // try to fetch. This prevents mass downloading when the user is scrolling really fast
            double delayInSeconds = 0.25;
            dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
            dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
                if ([self isIndexPathVisible:indexPath]) {
                    [SDWebImageDownloader.sharedDownloader
                     downloadImageWithURL:[NSURL URLWithString:item.image_url]
                     options:0
                     progress:^(NSUInteger receivedSize, long long expectedSize) { }
                     completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished) {
                         if (image && finished) {
                             [cell setCustomImage:image];
                             [[SDImageCache sharedImageCache] storeImage:image forKey:item.image_url];
                         }
                     }];
                }
            });
        }
    }];

    // Check if we are almost at the end of the scroll. If so, start fetching.
    // Doing this here is better than overriding scrollViewDidEndDragging
    if (indexPath.row >= [self.tableView numberOfRowsInSection:0] - 3) {
        [self refill];
    }

    return cell;
}

#pragma mark - Table view delegate

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    id sectionInfo = [_fetchedResultsController.sections objectAtIndex:section];
    NSInteger ret = [sectionInfo numberOfObjects];
    self.hasContent = (ret != 0);
    return ret;
}


# pragma mark - NSFetchedResultsControllerDelegate

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

- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
           atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {
    NSLog(@"2");
    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 {
    NSLog(@"3");
    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:
            [NSException raise:@"Unknown update" format:@"NSFetchedResultsChangeUpdate: invoked"];
            // [self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
            break;

        case NSFetchedResultsChangeMove:
            [NSException raise:@"Unknown update" format:@"NSFetchedResultsChangeMove: invoked"];
            [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
                             withRowAnimation:UITableViewRowAnimationFade];
            [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
                             withRowAnimation:UITableViewRowAnimationFade];
            break;
    }
}

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

(請注意,我省略了一些信息以使這個問題簡短)

這是LikedVC的fetchRequest

- (NSFetchRequest *)getFetchRequest {
    NSManagedObjectContext *moc = [(AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Item" inManagedObjectContext:moc];
    [request setPredicate:[NSPredicate predicateWithFormat:@"liked == %d OR origin == %d", 1, OriginsLike]];
    [request setEntity:entity];
    [request setResultType:NSManagedObjectResultType];
    [request setFetchBatchSize:10];
    NSSortDescriptor *d = [[NSSortDescriptor alloc] initWithKey:@"updated_at" ascending:NO selector:nil];
    [request setSortDescriptors:[NSArray arrayWithObject:d]];
    return request;
}

我看到一個用戶喜歡某個項目的錯誤,但是當用戶切換到LikedVC時,該項目未顯示在任何地方。

我在tableView的controllerDidChangeContent,controllerWillChangeContent等方法中添加了NSLog(@“ 1”),NSLog(@“ 2”),...。 我完全看不到“ 1”,“ 2”,..。

為什么我的NSFetchedResultsController不起作用?

直接回答

您問題的直接答案是,您需要觀察NSManagedObjectContextDidSaveNotification並將合並從您的臨時上下文到NSFetchedResultsController正在觀察的主上下文中的更改。 最好在擁有您的主要上下文的任何對象中執行此操作。 看起來您正在使用應用程序委托。

您合並數據,如下所示:

- (void)managedObjectContextDidSave:(NSNotification *)notification
{
    // if the notification is on a background thread, forward it to the main thread
    if (![NSThread isMainThread]) {
        [self performSelectorOnMainThread:@selector(managedObjectContextDidSave:) withObject:notification];
        return;
    }

    // if a context other than the main context has saved, merge the changes
    if (notification.object != self.managedObjectContext) {
        [self.managedObjectContext mergeChangesFromContextDidSaveNotification:notification];
    }
}

您的代碼似乎可疑

您的所有邏輯都發生在主隊列上,因此您根本不需要其他上下文。 在這種情況下,在主隊列上使用dispatch_async也沒有真正的好處。 如果將所有內容保留在主線程中,則只需創建新對象並將其直接保存到主上下文中即可。 如果您確實希望事情是異步的,請使用dispatch_async將塊分派到后台隊列,然后您需要為后台線程提供一個新的上下文(已實現)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM