2016-12-14 68 views
1

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

在我的视图控制器中,我使用“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 
}() 

在viewDidLoad方法,我写下面的代码来获取:

do { 
    try self.inspirationsResults.performFetch() 
} catch { 
    let fetchError = error as NSError 
    print("\(fetchError), \(fetchError.userInfo)") 
} 

我还添加委托方法“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)") 
      } 
     } 
    }) 
} 

我错过了什么?

+0

没有你的背景和'mergeChangesFromContextDidSaveNotification连接主线程上下文: '?或者它是父子连接? – bteapot

+0

@bteapot,我用“保存”细节更新了我的代码。因为我已经将后台任务的mergePolicy设置为NSMergeByPropertyObjectTrumpMergePolicy – Satyam

+0

'mergePolicy'没有在你的问题中扮演任何角色。我会写一个答案。 – bteapot

回答

0

从同一个持久性存储区绘制数据的两个或多个NSManagedObjectContext不会自动注意到该存储区中的更改。他们需要彼此链接以接收在其中一个中执行的删除,插入和更新。这种链接可以通过以下两种方式之一来建立:

  1. 合并更改。
  2. 使用父/子模式。

合并更改通常这样做:

// 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有它自己的特性,如thisthis

最后,如果您必须导入相对较大的数据集和几个几十个对象,请在保存和导入数据时断开NSFetchedResultsController的连接。这将为您节省大量的主线程处理时间。这里的方案:

[[NSNotificationCenter defaultCenter] postNotificationName:DBWillUpdateDataNotification object:self]; 
  • 保存更改并把它们合并到主背景下,或将它们推入父:

    1. 在处理你的数据,节省您的背景情况下前右门柱自己的通知上下文并保存。

    2. 帖子另一个通知:

      [[NSNotificationCenter defaultCenter] postNotificationName:DBDidUpdateDataNotification object:self]; 
      
    3. 手表在您的视图控制器这两个自定义通知。当收到第一个时,请在NSFetchedResultsController上设置delegatenil。这将阻止他们改变上下文并报告他们。当接收第二 - 连接你的代表回来,叫你的FRCS -performFetch:并重新加载你的接口,即呼吁表视图-reloadData,重新填充自定义标签等

  • +0

    如果我手动创建上下文,这将工作。但是我使用的是“persistentContainer.performBackgroundTask({(bgContext)in”,它将为我们提供上下文,我尝试设置mainContext作为闭包中可用的“bgContext”,但它的崩溃作为协调器已经为此设置了 – Satyam

    +0

    创建你自己的上下文不是问题,它只是几行。 – bteapot

    +0

    我正在关注“合并更改”。我有多个并行运行的backgroundContexts来更新数据。但是我使用主要的托管对象上下文来更新UI在我的情况下,通知也被调用,但是数据在获取的结果控制器中没有更新。 – Satyam