带有NSFetchedResultsController的UICollectionViewController损坏了,当所有对象的update属性,如果控制器对此属性进行了排序

问题描述 投票:0回答:1

我有UICollectionViewController,它是地图上某些对象的集合视图。因此,我可以更新获取结果中的一项(例如,为此对象之一设置LIKE)。很好我立即看到变化。但是,如果尝试更新所有对象的属性“ Distance”,并且在该属性上对同一时间控制器进行排序。提取的结果不会自动更新。然后,在滚动时,未更新的重复使用的单元格未更新,在启动所有对象的update属性之前,我只能看到显示的单元格。如果我连续TouchTouchInside第一个单元格,例如,将是Object 123,则该对象的另一个对象的控制器打开详细信息页面应在collectionView reloadData之后保持在此位置。


如果我要更改[fetchRequest setSortDescriptors:@[distanceAscending]];中的排序描述符

[fetchRequest setSortDescriptors:@[titleAscending]];

[fetchRequest setSortDescriptors:@[titleAscending, distanceAscending]];

很好。


如果我仅更改一个对象的属性“ Distance”。很好控制器对单元格进行应有的排序。


如果我要更改这次未获取的对象的属性“ Distance”。很好用。


如果我要关闭此控制器,然后再次打开[fetchRequest setSortDescriptors:@[distanceAscending]];,则应能正常工作


我想以不同的方式重新加载CollectionViewController,[self.collectionView reloadData];reloadwithPredicateDefault。但结果相同。


我想更改托管对象上下文NSPrivateQueueConcurrencyTypeNSMainQueueConcurrencyType。但结果相同。


MapObjectCollectionViewController.h

@interface MapObjectCollectionViewController : UICollectionViewController

MapObjectCollectionViewController.m

@interface MapObjectCollectionViewController ()<NSFetchedResultsControllerDelegate>

@property (strong, nonatomic) NSFetchedResultsController *fetchedResultsController;
@property (strong, nonatomic) NSManagedObjectContext* managedObjectContext;
@property (strong, nonatomic) NSPredicate * predicate1;
@property (strong, nonatomic) NSPredicate * predicate2;

#pragma mark - Fetched results controller

- (NSFetchedResultsController *)fetchedResultsController
{


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

    NSFetchRequest* fetchRequest = [[NSFetchRequest alloc] init];

    NSEntityDescription* description =
    [NSEntityDescription entityForName:@"MapObj"
                inManagedObjectContext:self.managedObjectContext];

    [fetchRequest setEntity:description];



        _predicate1 = [NSPredicate predicateWithFormat:@"types.typeObjValue IN %@", self.selectionsTypes];
        _predicate2 = [NSPredicate predicateWithFormat:@"wiFi >= %i", 0]; 



    NSPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates:@[_predicate1, _predicate2]];
    [fetchRequest setPredicate:predicate];


    NSSortDescriptor* titleAscending = [[NSSortDescriptor alloc] initWithKey:@"title" ascending:YES];

    NSSortDescriptor* distanceAscending = [[NSSortDescriptor alloc] initWithKey:@"distance" ascending:NO];

        [fetchRequest setSortDescriptors:@[distanceAscending]];
        // [fetchRequest setSortDescriptors:@[titleAscending, distanceAscending]];

    NSFetchedResultsController *aFetchedResultsController =
    [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
                                        managedObjectContext:self.managedObjectContext
                                          sectionNameKeyPath:nil
                                                   cacheName:nil];
    aFetchedResultsController.delegate = self;
    self.fetchedResultsController = aFetchedResultsController;

    NSError *error = nil;
    if (![self.fetchedResultsController performFetch:&error]) {

        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();
    }

    return _fetchedResultsController;
}

-(void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
      atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
     newIndexPath:(NSIndexPath *)newIndexPath {
    NSMutableDictionary *change = [[NSMutableDictionary alloc] init];
    switch(type) {
        case NSFetchedResultsChangeInsert:
            change[@(type)] = newIndexPath;
            break;
        case NSFetchedResultsChangeDelete:
            change[@(type)] = indexPath;
            break;
        case NSFetchedResultsChangeUpdate:
            change[@(type)] = indexPath;
            break;
        case NSFetchedResultsChangeMove:
            change[@(type)] = @[indexPath, newIndexPath];
            break;
    }
    [_itemChanges addObject:change];
}
- (void)reloadwithPredicateDefault {
    [NSFetchedResultsController deleteCacheWithName:nil];
    self.fetchedResultsController = nil;
    [self.fetchedResultsController performFetch:nil];
    [self.collectionView reloadData];
}
#pragma mark - UICollectionViewDataSource
- (NSManagedObjectContext*) managedObjectContext {

    if (!_managedObjectContext) {
        _managedObjectContext = [[DataManager sharedManager] managedObjectContext];
    }
    return _managedObjectContext;
}

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller{
    [self.collectionView performBatchUpdates:^{
        for (NSDictionary *change in self->_sectionChanges) {
            [change enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
                NSFetchedResultsChangeType type = [key unsignedIntegerValue];
                switch(type) {
                    case NSFetchedResultsChangeInsert:
                        [self.collectionView insertSections:[NSIndexSet indexSetWithIndex:[obj unsignedIntegerValue]]];
                        break;
                    case NSFetchedResultsChangeDelete:
                        [self.collectionView deleteSections:[NSIndexSet indexSetWithIndex:[obj unsignedIntegerValue]]];
                        break;
                    case NSFetchedResultsChangeMove:
                        break;
                    case NSFetchedResultsChangeUpdate:
                        break;
                }
            }];
        }
        for (NSDictionary *change in self->_itemChanges) {
            [change enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
                NSFetchedResultsChangeType type = [key unsignedIntegerValue];
                switch(type) {
                    case NSFetchedResultsChangeInsert:
                        [self.collectionView insertItemsAtIndexPaths:@[obj]];
                        break;
                    case NSFetchedResultsChangeDelete:
                        [self.collectionView deleteItemsAtIndexPaths:@[obj]];
                        break;
                    case NSFetchedResultsChangeUpdate:
                        [self.collectionView reloadItemsAtIndexPaths:@[obj]];
                        break;
                    case NSFetchedResultsChangeMove:
                        [self.collectionView moveItemAtIndexPath:obj[0] toIndexPath:obj[1]];
                        break;
                }
            }];
        }
    } completion:^(BOOL finished) {
        self->_sectionChanges = nil;
        self->_itemChanges = nil;

    }];
}

DataManager.h

@property (readonly, strong, nonatomic) NSManagedObjectContext *mainPrivateManagedObjectContext;
@property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
@property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
@property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;

+ (DataManager*)sharedManager;

DataManager.m

@implementation DataManager

@synthesize mainPrivateManagedObjectContext = _mainPrivateManagedObjectContext;
@synthesize managedObjectContext = _managedObjectContext;
@synthesize managedObjectModel = _managedObjectModel;
@synthesize persistentStoreCoordinator = _persistentStoreCoordinator;

+(DataManager*) sharedManager{

    static DataManager* manager = nil;

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        manager = [[DataManager alloc] init];
    });

    return manager;
}

#pragma mark - Core Data stack


- (NSManagedObjectModel *)managedObjectModel {
    // The managed object model for the application. It is a fatal error for the application not to be able to find and load its model.
    if (_managedObjectModel != nil) {
        return _managedObjectModel;
    }
    NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"ProjectName" withExtension:@"momd"];
    _managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
    return _managedObjectModel;
}

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
    // The persistent store coordinator for the application. This implementation creates and returns a coordinator, having added the store for the application to it.
    if (_persistentStoreCoordinator != nil) {
        return _persistentStoreCoordinator;
    }

    // Create the coordinator and store

    _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
    NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"ProjectName.sqlite"];
    NSError *error = nil;
    // NSString *failureReason = @"There was an error creating or loading the application's saved data.";
    if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:@{NSMigratePersistentStoresAutomaticallyOption:@YES, NSInferMappingModelAutomaticallyOption:@YES} error:&error]) {
        NSLog(@"error = %@", error);
        // Report any error we got.

        [[NSFileManager defaultManager] removeItemAtURL:storeURL error:nil]; //Удалить старую базу

        [_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]; //Создать базу заново

    }
    return _persistentStoreCoordinator;
}


- (NSManagedObjectContext *)managedObjectContext {

    // Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.)
    if (_managedObjectContext != nil) {
        // . NSLog(@"get managedObjectContext");
        return _managedObjectContext;
    }

    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
    if (!coordinator) {
        return nil;
    }

    _mainPrivateManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
    [_mainPrivateManagedObjectContext setPersistentStoreCoordinator:coordinator];

    _managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
    [_managedObjectContext setParentContext:_mainPrivateManagedObjectContext];
    // . NSLog(@"get return managedObjectContext");
    return _managedObjectContext;
}

- (NSManagedObjectContext *)getContextForBGTask {
    NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];

    [context setParentContext:self.managedObjectContext];
    return context;
}
- (NSArray*) allMapObj {

    NSFetchRequest* request = [[NSFetchRequest alloc] init];

    NSEntityDescription* description =
    [NSEntityDescription entityForName:@"MapObj"
                inManagedObjectContext:self.managedObjectContext];

    [request setEntity:description];

    NSError* requestError = nil;
    NSArray* resultArray = [self.managedObjectContext executeFetchRequest:request error:&requestError];
    if (requestError) {
        NSLog(@"%@", [requestError localizedDescription]);
    }
    return resultArray;
}
- (void)calculateDistanceWithCurrentLoaction:(CLLocation*) currentLoaction{
    NSFetchRequest* request = [[NSFetchRequest alloc] init];
    NSManagedObjectContext * bgcontext = [self getContextForBGTask];
    NSEntityDescription* description =
    [NSEntityDescription entityForName:@"MapObj"
                inManagedObjectContext:bgcontext];

    [request setEntity:description];

    NSError* requestError = nil;
    NSArray* resultArray = [bgcontext executeFetchRequest:request error:&requestError];
    if (requestError) {
        NSLog(@"%@", [requestError localizedDescription]);
    }
    for (MapObj *mapObject in resultArray) {

            CLLocation *endLocation = [[CLLocation alloc] initWithLatitude:[mapObject.latitude doubleValue] longitude:[mapObject.longitude doubleValue]];
            CLLocationDistance distance = [currentLoaction distanceFromLocation:endLocation];
            mapObject.distance = [NSDecimalNumber decimalNumberWithString:[NSString stringWithFormat:@"%f", distance]];

    }
    [bgcontext updatedObjects];
    [self saveContextForBGTask:bgcontext];

}
- (void)saveContextForBGTask:(NSManagedObjectContext *)bgTaskContext {
    if (bgTaskContext.hasChanges) {

        [bgTaskContext performBlockAndWait:^{
            NSError *error = nil;

            [bgTaskContext save:&error];
        }];
        // Save default context
        [self saveDefaultContext:YES];
    }
}
- (void)saveDefaultContext:(BOOL)wait {
    if (_managedObjectContext.hasChanges) {
        [_managedObjectContext performBlockAndWait:^{
            // . NSLog(@"managed context = %@", _managedObjectContext);
            NSError *error = nil;
            [self->_managedObjectContext save:&error];
        }];
    }
    void (^saveMainPrivateManagedObjectContext) (void) = ^{
        NSError *error = nil;
        [self->_mainPrivateManagedObjectContext save:&error];
    };
    if ([_mainPrivateManagedObjectContext hasChanges]) {
        if (wait){
            // . NSLog(@"main context = %@", _mainPrivateManagedObjectContext);
            [_mainPrivateManagedObjectContext performBlockAndWait:saveMainPrivateManagedObjectContext];
        } else {
            [_mainPrivateManagedObjectContext performBlock:saveMainPrivateManagedObjectContext];
        }
    }
}

控制台错误:

2019-09-28 12:35:14.951873+0400 ProjectName[15695:4020487] *** Assertion failure in -[UICollectionView _endItemAnimationsWithInvalidationContext:tentativelyForReordering:animator:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKitCore/UIKit-3698.140/UICollectionView.m:5972

CoreData: fault: Serious application error.  An exception was caught from the delegate of NSFetchedResultsController during a call to -controllerDidChangeContent:.  attempt to perform an insert and a move to the same index path (<NSIndexPath: 0xa945f4d4afea737e> {length = 2, path = 0 - 4}) with userInfo (null)

我错过了什么?

objective-c core-data nsfetchedresultscontroller
1个回答
0
投票

作为一个简单的解决方案,添加了:

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller{
   [self.collectionView performBatchUpdates:^{
   ......
   if(self->_itemChanges.count > 1){
       [self.collectionView reloadSections:[NSIndexSet indexSetWithIndex:0]];
   } else {
       for (NSDictionary *change in self->_itemChanges) {
           ...
       }
   }
   ......
}
© www.soinside.com 2019 - 2024. All rights reserved.