1

插入(NSFetchedResultsChangeInsert)從我的後臺線程工作,從同一個線程,使用相同的managedObjectContext保存合併的通知,因此不會得到正確保存,刪除不工作(在tableviews可見刪除動畫因而不工作)。爲什麼當對象被刪除時NSFetchedResultsChangeDelete不會被觸發?

更新,現在顯示的問題SchedulesViewController.m我的所有代碼:

#import "SchedulesViewController.h" 
#import "CustomScheduleTableViewCell.h" 
#import <QuartzCore/QuartzCore.h> 
#import "ViewScheduleViewController.h" 

@implementation SchedulesViewController { 
    NSIndexPath *deleteActionIndexPath; 
} 

@synthesize fetchedResultsController, managedObjectContext; 

- (id)initWithStyle:(UITableViewStyle)style 
{ 
    self = [super initWithStyle:style]; 
    if (self) { 
     // Custom initialization 
    } 
    return self; 
} 

- (void)didReceiveMemoryWarning 
{ 
    // Releases the view if it doesn't have a superview. 
    [super didReceiveMemoryWarning]; 

    // Release any cached data, images, etc that aren't in use. 
} 

#pragma mark - View lifecycle 

- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 

    if (managedObjectContext == nil) { 
     managedObjectContext = [(AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext]; 
    } 

    self.title = @"Schema's"; 

    // Uncomment the following line to display an Edit button in the navigation bar for this view controller. 
    self.navigationItem.leftBarButtonItem = self.editButtonItem; 

    NSError *error = nil; 
    if (![[self fetchedResultsController] performFetch:&error]) { 
     /* 
     Replace this implementation with code to handle the error appropriately. 

     abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button. 
     */ 
     NSLog(@"Unresolved error %@, %@", error, [error userInfo]); 
     abort(); 
    } 
} 

- (void)viewDidUnload 
{ 
    [super viewDidUnload]; 
    // Release any retained subviews of the main view. 
    // e.g. self.myOutlet = nil; 
    //storedReminderSchedules = nil; 
    //context = nil; 
} 

- (void)viewWillAppear:(BOOL)animated 
{ 
    [super viewWillAppear:animated]; 
    NSLog(@"Schedules viewWillAppear"); 
} 

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

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

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

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation 
{ 
    return YES; 
} 

#pragma mark - Table view data source 
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 
{ 
    return [[fetchedResultsController sections] count]; 
} 

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { 
    // check if we really have any sections in the managed object: 
    //if (!fetchedResultsController.sections.count) return @"Persoonlijk"; 

    id <NSFetchedResultsSectionInfo> sectionInfo = [[fetchedResultsController sections] objectAtIndex:section]; 
    return [sectionInfo name]; 
} 

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 
{ 
    // check if we really have any sections in the managed object: 
    //if (!fetchedResultsController.sections.count) return 0; 

    id <NSFetchedResultsSectionInfo> sectionInfo = [[fetchedResultsController sections] objectAtIndex:section]; 
    return [sectionInfo numberOfObjects]; 
} 

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    static NSString *CellIdentifier = @"ScheduleCell"; 
    CustomScheduleTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 

    [self configureCell:cell atIndexPath:indexPath]; 
    return cell; 
} 


// Override to support conditional editing of the table view. 
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    return YES; 
} 

- (void)configureCell:(CustomScheduleTableViewCell*)cell atIndexPath:(NSIndexPath*)indexPath { 
    // Configure the cell. 
    ReminderSchedule *reminderSchedule = [fetchedResultsController objectAtIndexPath:indexPath]; 
    cell.name.text = reminderSchedule.name; 
} 

#pragma mark - Table view delegate 

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    if ([[[[fetchedResultsController sections] objectAtIndex:indexPath.section] name] hasPrefix:@"Dr."]) { 
     ViewScheduleViewController *controller = [[ViewScheduleViewController alloc]init]; 
     controller.reminderSchedule = [self.fetchedResultsController objectAtIndexPath:[self.tableView indexPathForSelectedRow]]; 
     controller.context = [fetchedResultsController managedObjectContext]; 
     [self.navigationController pushViewController:controller animated:YES]; 
    } 
    else { 
     [self performSegueWithIdentifier:@"EditSchedule" sender:self]; 
    } 
} 

- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath { 
    return UITableViewCellEditingStyleDelete; 
} 

#pragma mark - ModifyScheduleViewControllerDelegate 
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender 
{ 
    if ([segue.identifier isEqualToString:@"AddSchedule"]) 
    { 
     [TestFlight passCheckpoint:@"Add Schedule"]; 
     // Get reference to the destination view controller 
     UINavigationController *navigationController = segue.destinationViewController; 
     ModifyScheduleViewController *controller = [[navigationController viewControllers] objectAtIndex:0]; 
     controller.delegate = self; 
     controller.fetchedResultsController = self.fetchedResultsController; 
     [controller.navigationItem.rightBarButtonItem setEnabled:NO]; 
    } 
    else if ([segue.identifier isEqualToString:@"EditSchedule"]) { 
     [TestFlight passCheckpoint:@"Edit Schedule"]; 
     // Get reference to the destination view controller 
     UINavigationController *navigationController = segue.destinationViewController; 
     ModifyScheduleViewController *controller = [[navigationController viewControllers] objectAtIndex:0]; 
     controller.delegate = self; 
     controller.fetchedResultsController = self.fetchedResultsController; 
     controller.reminderSchedule = [self.fetchedResultsController objectAtIndexPath:[self.tableView indexPathForSelectedRow]]; 
    } 
} 

#pragma mark - 
#pragma mark Fetched results controller 

- (NSFetchedResultsController *)fetchedResultsController { 

    if (fetchedResultsController != nil) { 
     return fetchedResultsController; 
    } 

    /* 
    Set up the fetched results controller. 
    */ 
    // Create the fetch request for the entity. 
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init]; 
    // Edit the entity name as appropriate. 
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"ReminderSchedule" inManagedObjectContext:managedObjectContext]; 
    [fetchRequest setEntity:entity]; 

    // Set the batch size to a suitable number. 
    [fetchRequest setFetchBatchSize:20]; 

    // Edit the sort key as appropriate. 
    NSSortDescriptor *sortDescriptorName = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:NO]; 
    NSSortDescriptor *sortDescriptorSection = [[NSSortDescriptor alloc] initWithKey:@"sectionName" ascending:YES]; 
    NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptorSection,sortDescriptorName, nil]; 

    [fetchRequest setSortDescriptors:sortDescriptors]; 

    // Edit the section name key path and cache name if appropriate. 
    // nil for section name key path means "no sections". 
    NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:@"sectionName" cacheName:@"Root"]; 
    aFetchedResultsController.delegate = self; 
    self.fetchedResultsController = aFetchedResultsController; 

    return fetchedResultsController; 
}  

- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo 
      atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type { 
    NSLog(@"didChangeSection"); 
    switch(type) { 
     case NSFetchedResultsChangeUpdate: 
      [self.tableView reloadData]; 
      break; 
     case NSFetchedResultsChangeInsert: 
      [self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade]; 
      break; 
     case NSFetchedResultsChangeDelete: 
      [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade]; 
      break; 
    } 
} 


// Notifies the delegate that section and object changes are about to be processed and notifications will be sent. 
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller { 
    [self.tableView beginUpdates]; 
} 

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

    switch (type) { 
     case NSFetchedResultsChangeInsert: 
      [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] 
            withRowAnimation:UITableViewRowAnimationFade]; 
      break; 
     case NSFetchedResultsChangeUpdate: 
      [self configureCell:(CustomScheduleTableViewCell *)[self.tableView cellForRowAtIndexPath:indexPath] 
        atIndexPath:indexPath]; 
      break; 
     case NSFetchedResultsChangeDelete: 
      [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] 
            withRowAnimation:UITableViewRowAnimationFade]; 
      break; 
     case NSFetchedResultsChangeMove: 
      [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] 
            withRowAnimation:UITableViewRowAnimationFade]; 
      [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] 
            withRowAnimation:UITableViewRowAnimationFade]; 
      break;  
     default: 
      break; 
    } 
} 

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

- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation duration:(NSTimeInterval)duration { 
    [self.tableView reloadData]; 
} 

@end 

更新 額外的信息:

插入和對象的缺失在同一個線程發生了,他們使用相同的managedObjectContext並使用相同的保存庫。事實上,第一個對象被插入然後其他被刪除,然後有一個保存。

我使用NSManagedObjectContextDidSaveNotification觸發在AppDelegate中的保存合併保存。

更新 更多代碼:

AppDelegate.m

- (void)saveContext 
{ 
    NSError *error = nil; 
    NSManagedObjectContext *managedObjectContext = self.managedObjectContext; 
    if (managedObjectContext != nil) { 
     if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) { 
      // Replace this implementation with code to handle the error appropriately. 
      // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. 
      NSLog(@"Unresolved error %@, %@", error, [error userInfo]); 
      abort(); 
     } 
    } 
} 

-(void)mergeChanges:(NSNotification *)saveNotification { 
    NSLog(@"Merging and saving"); 
    if ([NSThread isMainThread]) 
     [self.managedObjectContext mergeChangesFromContextDidSaveNotification:saveNotification]; 
    else 
     [self performSelectorOnMainThread:@selector(mergeChanges:) withObject:saveNotification waitUntilDone:NO]; 
} 

BacgroundThread.m

AppDelegate *theDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate]; 
NSNotificationCenter *notify = [NSNotificationCenter defaultCenter]; 

NSManagedObjectContext *newMoc = [[NSManagedObjectContext alloc] init]; 
[newMoc setPersistentStoreCoordinator:[theDelegate persistentStoreCoordinator]]; 

//縮短插入部爲了說明(插入工作良好)

ReminderSchedule *reminderSchedule = [NSEntityDescription insertNewObjectForEntityForName:[entity name] inManagedObjectContext:newMoc]; 

//實際插入後刪除部分代碼

if ([jsonDict objectForKey:@"deletedSchedules"] != nil) { 
    NSArray *deletedSchedules = [jsonDict objectForKey:@"deletedSchedules"]; 

    for (int i = 0; i < [deletedSchedules count]; i++) { 
     NSInteger externalScheduleID = [[deletedSchedules objectAtIndex:i] intValue]; 

     NSLog(@"Removing schedule %d", externalScheduleID); 

     NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init]; 
     NSEntityDescription *entity = [NSEntityDescription entityForName:@"ReminderSchedule" inManagedObjectContext:newMoc]; 
     [fetchRequest setEntity:entity]; 

     NSPredicate *predicate = [NSPredicate predicateWithFormat:@"externalScheduleID == %d", externalScheduleID]; 
     [fetchRequest setPredicate:predicate]; 

     NSError *error = nil; 
     NSArray *result = [newMoc executeFetchRequest:fetchRequest error:&error]; 

     for (NSManagedObject *managedObject in result) { 
      [newMoc deleteObject:managedObject]; 
     } 
    } 
} 

//保存和刪除

NSError *saveError = nil; 
if (![newMoc save:&saveError]) { 
    NSLog(@"Whoops, couldn't save: %@", [saveError localizedDescription]); 
} 


[[NSNotificationCenter defaultCenter] removeObserver:theDelegate];   

更新 做的插入和刪除,然後保存工作。做插入本身的作品。自己做刪除操作不起作用。

+0

其他回調(插入,更新)是否工作? – Mundi

+0

是插入工作正常。委託函數被調用並插入動畫效果很好。 – Pieter

+0

我假定刪除本身不工作(被刪除的記錄仍然顯示在數據庫中)。發佈相關代碼。 – Mundi

回答

0

確保NSNotificationCenter八方通獲取與NSManagedObjectContextDidSaveNotification註冊,不僅當有東西插入。

0

確保self.fetchResultsController.delegate = self;是你viewDidLoad方法內。

+0

代表被設置爲自己。否則,將不會調用委託方法。 – Pieter

相關問題