2013-04-10 119 views
1

當我的用戶看到一張圖片時,她可以通過按下按鈕來欣賞它。下面的代碼運行:NSFetchedResultsController不更新?

- (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不工作?

+0

我讀了幾次,所以我不得不問:你期望在這裏發生什麼? 用戶點擊,並且' - (void)feedback:(Item *)item isLiked:(bool)liked'被觸發,並且您聲明它正常工作。 'LikedViewController'拿起剛剛喜歡的'Item'。你的照片來自哪裏?它是否在'Item'對象中被引用? 我不是一個NSFetchedController的巨大專家,但我不認爲它是按照您期望的方式觸發事件,並且似乎它的工作方式如果'喜歡'結果正確返回....? – 2013-04-11 00:23:37

+0

@twairball更新了問題。基本上,反饋被解僱。我**認爲**反饋已正確執行。 NSFetchedResultsController沒有更新,因爲我沒有看到正在顯示的新項目 – disappearedng 2013-04-11 00:33:24

回答

0

直接回答

直接回答你的問題是,你需要觀察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將塊分派到後臺隊列,然後你需要一個新的上下文(如你已經實現的)用於後臺線程。

+0

謝謝! 工作 – disappearedng 2013-04-11 08:45:04

相關問題