1

Insert (NSFetchedResultsChangeInsert) 동일한 managedObjectContext 저장 병합 알림을 사용하여 동일한 스레드에서 내 백그라운드 스레드에서 작동하므로 삭제가 올바르게 작동하지 않습니다 (테이블 뷰에서 보이는 삭제 애니메이션이 작동하지 않음).왜 개체가 삭제되면 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를 사용하고 동일한 저장 소를 사용하십시오. 실제로 첫 번째 객체가 삽입 된 다음 다른 객체가 삭제 된 다음 하나의 저장이 삭제됩니다.

AppDelegate에서 저장 병합을 트리거하는 NSManagedObjectContextDidSaveNotification을 사용하여 저장합니다.

업데이트 더 코드 :

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]]; 

// 단축 삽입 부분은 그림을 위해 (삽입이 잘 작동) 삭제 섹션 코드

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];   

업데이트 삭제하고 삭제 한 후 작업을 저장

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

// 실제. 삽입 자체가 작동합니다. 그 자체로 삭제를하는 것은 걱정하지 않습니다.

+0

다른 콜백 (삽입, 업데이트)이 작동합니까? – Mundi

+0

예 삽입 작업이 정상적으로 작동합니다. 델리게이트 함수가 호출되고 삽입 애니메이션이 잘 작동합니다. – Pieter

+0

삭제 자체가 작동하지 않는다고 가정합니다 (삭제 된 레코드가 여전히 데이터베이스에 표시됨). 관련 코드를 게시하십시오. – Mundi

답변

0

삽입 할 항목이있을 때뿐만 아니라 NSNotificationCenter가 항상 NSManagedObjectContextDidSaveNotification에 등록되었는지 확인하십시오.

0

self.fetchResultsController.delegate = self;viewDidLoad 방법 안에 있는지 확인하십시오.

+0

대리인이 자기로 설정됩니다. 델리게이트 메쏘드는 insert와 함께 호출되지 않을 것이다. – Pieter

관련 문제