Ios Coredata performFetch未获取新记录

Ios Coredata performFetch未获取新记录,ios,core-data,swift3,nsfetchedresultscontroller,Ios,Core Data,Swift3,Nsfetchedresultscontroller,我在iOS10(Swift3)应用程序中集成了coredata框架,该应用程序从服务器和显示器中提取数据。当应用程序第一次启动时,核心数据没有任何记录。在与服务器交换一些信息后,它开始在后台线程中同步。我能够看到数据通过web服务从服务器下载,解析并存储在核心数据中。但如果我退出并启动应用程序,它会显示所有记录 在我的视图控制器中,我使用“NSFetchedResultsController”在“TableView”中显示记录。我正在创建获取结果控制器,如下所示: fileprivate laz

我在iOS10(Swift3)应用程序中集成了coredata框架,该应用程序从服务器和显示器中提取数据。当应用程序第一次启动时,核心数据没有任何记录。在与服务器交换一些信息后,它开始在后台线程中同步。我能够看到数据通过web服务从服务器下载,解析并存储在核心数据中。但如果我退出并启动应用程序,它会显示所有记录

在我的视图控制器中,我使用“NSFetchedResultsController”在“TableView”中显示记录。我正在创建获取结果控制器,如下所示:

fileprivate lazy var inspirationsResults: NSFetchedResultsController<Inspiration> = {
    // Create Fetch Request
    let fetchRequest: NSFetchRequest<Inspiration> = Inspiration.fetchRequest()

    // Configure Fetch Request
    fetchRequest.sortDescriptors = [NSSortDescriptor(key: "timeStamp", ascending: false)]

    // Create Fetched Results Controller
    let fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: CoreDataManager.shared.getContext(), sectionNameKeyPath: nil, cacheName: nil)

    // Configure Fetched Results Controller
    fetchedResultsController.delegate = self

    return fetchedResultsController
}()
我还添加了委托方法“controllerWillChangeContent、controllerDidChangeContent和didChangeObject”来处理更新/修改

我正在使用persistentContainer保存对象:

 func addInspirations(_ inspirations:[[String: AnyObject]]) {
    persistentContainer.performBackgroundTask({ (bgContext) in
        bgContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy

        for tInspiration in inspirations {
            let inspiration = Inspiration(context: bgContext)
            inspiration.inspirationID = tInspiration[kInspirationId] as! Int32
            inspiration.inspirationName = tInspiration[kInspirationName] as? String
        }

        if bgContext.hasChanges {
            do {
                try bgContext.save()
            } catch {
                let nserror = error as NSError
                fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
            }
        }
    })
}

我遗漏了什么吗?

从同一个持久存储中提取数据的两个或多个
NSManagedObjectContext
s没有自动注意到该存储中的更改。它们需要相互链接以接收其中一个中执行的删除、插入和更新。可通过以下两种方式之一建立此类链接:

  • 合并更改
  • 使用父/子模式
  • 合并更改通常这样做:

    // initializing your contexts
    self.mainContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
    self.mainContext.persistentStoreCoordinator = self.coordinator;
    
    self.backgroundContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
    self.backgroundContext.persistentStoreCoordinator = self.coordinator;
    
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mainContextDidSave:) name:NSManagedObjectContextDidSaveNotification object:self.mainContext];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(backgroundContextDidSave:) name:NSManagedObjectContextDidSaveNotification object:self.backgroundContext];
    
    后来的某个地方:

    - (void)mainContextDidSave:(NSNotification *)notification
    {
        [self.backgroundContext performBlock:^{
            [self.backgroundContext mergeChangesFromContextDidSaveNotification:notification];
        }];
    }
    
    - (void)backgroundContextDidSave:(NSNotification *)notification
    {
        [self.mainContext performBlock:^{
            [self.mainContext mergeChangesFromContextDidSaveNotification:notification];
        }];
    }
    
    这样可以确保您的上下文在更改持续后立即收到更改

    可能的父/子设置变体之一是:

    // when initializing your contexts
    self.mainContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
    self.mainContext.persistentStoreCoordinator = self.coordinator;
    
    self.backgroundContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
    self.backgroundContext.parentContext = self.mainContext;
    
    // when saving your background changes
    [self.backgroundContext performBlock:^{
        __block NSError *error;
    
        if ([self.backgroundContext save:&error) {
            [self.mainContext performBlock:^{
    
                if (![self.mainContext save:&error]) {
                    NSLog(@"Error saving main context");
                }
            }];
        } else {
            NSLog(@"Error saving background context");
        }
    }];
    
    这将把更改从后台上下文推送到主上下文,并将它们保存在持久存储中

    另外,
    NSFetchedResultsController
    也有自己的特性,比如or

    最后,如果必须导入包含大约几十个数千个对象的相对较大的数据集,请在保存和导入数据时断开
    NSFetchedResultsController
    s。这将为您节省大量的主线程处理时间。计划如下:

  • 处理数据时,请在保存背景上下文之前发布自己的通知:

    [[NSNotificationCenter defaultCenter] postNotificationName:DBWillUpdateDataNotification object:self];
    
  • 保存更改并将其合并到主上下文中,或将其推入父上下文并保存

  • 发布另一个通知:

    [[NSNotificationCenter defaultCenter] postNotificationName:DBDidUpdateDataNotification object:self];
    
  • 在视图控制器中注意这两个自定义通知。接收第一个代理时,在
    NSFetchedResultsController
    s上将
    代理设置为
    nil
    。这将阻止他们在上下文中寻找变化并报告它们。当收到第二个请求时-将您的代表连接回,在FRC上调用
    -performFetch:
    ,并重新加载界面,即在表视图上调用
    -reloadData
    ,重新填充自定义标签等


  • 从同一个持久存储中提取数据的两个或多个
    NSManagedObjectContext
    s不会自动注意到该存储中的更改。它们需要相互链接以接收其中一个中执行的删除、插入和更新。可通过以下两种方式之一建立此类链接:

  • 合并更改
  • 使用父/子模式
  • 合并更改通常这样做:

    // initializing your contexts
    self.mainContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
    self.mainContext.persistentStoreCoordinator = self.coordinator;
    
    self.backgroundContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
    self.backgroundContext.persistentStoreCoordinator = self.coordinator;
    
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mainContextDidSave:) name:NSManagedObjectContextDidSaveNotification object:self.mainContext];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(backgroundContextDidSave:) name:NSManagedObjectContextDidSaveNotification object:self.backgroundContext];
    
    后来的某个地方:

    - (void)mainContextDidSave:(NSNotification *)notification
    {
        [self.backgroundContext performBlock:^{
            [self.backgroundContext mergeChangesFromContextDidSaveNotification:notification];
        }];
    }
    
    - (void)backgroundContextDidSave:(NSNotification *)notification
    {
        [self.mainContext performBlock:^{
            [self.mainContext mergeChangesFromContextDidSaveNotification:notification];
        }];
    }
    
    这样可以确保您的上下文在更改持续后立即收到更改

    可能的父/子设置变体之一是:

    // when initializing your contexts
    self.mainContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
    self.mainContext.persistentStoreCoordinator = self.coordinator;
    
    self.backgroundContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
    self.backgroundContext.parentContext = self.mainContext;
    
    // when saving your background changes
    [self.backgroundContext performBlock:^{
        __block NSError *error;
    
        if ([self.backgroundContext save:&error) {
            [self.mainContext performBlock:^{
    
                if (![self.mainContext save:&error]) {
                    NSLog(@"Error saving main context");
                }
            }];
        } else {
            NSLog(@"Error saving background context");
        }
    }];
    
    这将把更改从后台上下文推送到主上下文,并将它们保存在持久存储中

    另外,
    NSFetchedResultsController
    也有自己的特性,比如or

    最后,如果必须导入包含大约几十个数千个对象的相对较大的数据集,请在保存和导入数据时断开
    NSFetchedResultsController
    s。这将为您节省大量的主线程处理时间。计划如下:

  • 处理数据时,请在保存背景上下文之前发布自己的通知:

    [[NSNotificationCenter defaultCenter] postNotificationName:DBWillUpdateDataNotification object:self];
    
  • 保存更改并将其合并到主上下文中,或将其推入父上下文并保存

  • 发布另一个通知:

    [[NSNotificationCenter defaultCenter] postNotificationName:DBDidUpdateDataNotification object:self];
    
  • 在视图控制器中注意这两个自定义通知。接收第一个代理时,在
    NSFetchedResultsController
    s上将
    代理设置为
    nil
    。这将阻止他们在上下文中寻找变化并报告它们。当收到第二个请求时-将您的代表连接回,在FRC上调用
    -performFetch:
    ,并重新加载界面,即在表视图上调用
    -reloadData
    ,重新填充自定义标签等


  • 您的后台和主线程上下文是否与
    mergeChangesFromContextDidSaveNotification:
    连接?还是父子连接?@bteapot,我用“保存”详细信息更新了代码。因为我已经将后台任务的mergePolicy设置为NSMergeByPropertyObjectTrumpMergePolicy
    mergePolicy
    在导致问题的原因中没有任何作用。我会写一个答案。是的,你是对的。合并策略只是为了在保存时解决冲突。因此@bteapot的问题仍然是:这两个上下文(
    bgContext
    CoreDataManager.shared.getContext()
    )是如何关联的?显示每个线程的初始化。您的后台和主线程上下文是否与
    合并更改从ContextDidSaveNotification:
    连接?还是父子连接?@bteapot,我用“保存”详细信息更新了代码。因为我已经将后台任务的mergePolicy设置为NSMergeByPropertyObjectTrumpMergePolicy
    mergePolicy
    在导致问题的原因中没有任何作用。我会写一个答案。是的,你是对的。合并策略只是为了在保存时解决冲突。因此@bteapot的问题仍然是:这两个上下文(
    bgContext
    CoreDataManager.shared.getContext()
    )是如何关联的?显示