如何保证主机应用程序和扩展使用的共享应用程序容器中的核心数据存储中的唯一条目?

为了有效地提出我的问题,我们首先考虑一下我正面临的确切情况:

一般设置

  • 主机iOS 8应用程序。
  • 与主机应用程序捆绑在一起的一个或多个iOS 8扩展(WatchKit,Share等)。
  • 主机应用程序和所有扩展在共享应用程序组容器中共享同一个Core Data SQLite存储。
  • 每个应用程序/扩展都有自己的NSPersistentStoreCoordinator和NSManagedObjectContext。
  • 每个持久性存储协调器都使用一个持久性存储,它与所有其他持久性存储共享组容器中相同的SQLite资源。
  • 应用程序和所有扩展使用通用代码库来同步Internet上的远程API资源的内容。

导致问题的事件序列

  1. 用户启动主机应用程序。 它开始从远程API资源获取数据。 核心数据模型对象是基于API响应创build的,并“插入”到主机应用程序的托pipe对象上下文中。 每个API实体都有一个唯一标识,在远程API后端标识它。 通过“upsert”,我的意思是,对于每个API实体,如果无法find给定uniqueID的现有条目,则主机应用程序仅在Core Data中创build一个新条目。

  2. 同时,用户还启动了一个主机应用程序的扩展。 它也从相同的远程API执行某种获取。 它还尝试在parsingAPI响应时执行“upsert”。

  3. 问题:如果主机应用程序和扩展同时尝试为同一个API实体插入Core Data条目,会发生什么情况? 要看看这可能是怎么回事,让我们来看一个upsert的事件序列:

核心数据上传序列:

  1. APIparsing代码parsing给定API实体的唯一ID。
  2. parsing器对任何与uniqueID等于parsing的uniqueID的谓词相匹配的条目执行Core Data提取。
  3. 如果找不到现有条目,parsing器将为此API实体插入一个新的Core Data条目,并将其uniqueID属性设置为parsing的uniqueID。
  4. parsing器保存托pipe的对象上下文,这会将新的条目数据向下推送到SQLite后备存储。

问题详细

假设主机应用程序和扩展程序独立parsing同一API实体的API响应。 如果主机应用程序和扩展程序都在步骤3完成之前到达步骤3,则它们都将尝试为相同的唯一ID插入新的核心数据条目。 当他们到达第4步并调用save:在各自的被pipe理对象上下文中时,Core Data将快乐地创build重复的条目。

据我所知,核心数据没有任何办法来标记一个属性是唯一的。 我需要一个与SQLite INSERT OR IGNORE + UPDATE组合相当的核心数据。 。 否则,我需要一种方法来“locking”持久存储的SQLite后备存储,这听起来像是一个麻烦的秘诀。

有没有一种已知的方法来解决iOS 8扩展引入的这个相当新颖的问题?

有没有一种已知的方法来解决iOS 8扩展引入的这个相当新颖的问题?

是的,与Core Data一起使用iCloud的方法是一样的:让副本发生,然后去清理它们。 这两种情况都有创build重复条目的风险,并且没有完全可靠的方法来防止它们。 由于你有一个uniqueID键,就这一点而言,你的状态很好。

正如戴夫·德隆(Dave DeLong)所指出的那样,要想避免这个问题就容易多了。 如果这是不可能的,你可以处理它,做一些额外的工作。

find重复的东西就像这样:

 NSError *error = nil; NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] init]; [moc setPersistentStoreCoordinator:self.persistentStoreCoordinator]; NSFetchRequest *fr = [[NSFetchRequest alloc] initWithEntityName:@"MyEntityName"]; [fr setIncludesPendingChanges:NO]; NSExpression *countExpr = [NSExpression expressionWithFormat:@"count:(uniqueID)"]; NSExpressionDescription *countExprDesc = [[NSExpressionDescription alloc] init]; [countExprDesc setName:@"count"]; [countExprDesc setExpression:countExpr]; [countExprDesc setExpressionResultType:NSInteger64AttributeType]; NSAttributeDescription *uniqueIDAttr = [[[[[_psc managedObjectModel] entitiesByName] objectForKey:@"MyEntityName"] propertiesByName] objectForKey:@"uniqueID"]; [fr setPropertiesToFetch:[NSArray arrayWithObjects:uniqueIDAttr, countExprDesc, nil]]; [fr setPropertiesToGroupBy:[NSArray arrayWithObject:uniqueIDAttr]]; [fr setResultType:NSDictionaryResultType]; NSArray *countDictionaries = [moc executeFetchRequest:fr error:&error]; 

这几乎是SQL中类似这样的核心数据:

 SELECT uniqueID, COUNT(uniqueID) FROM MyEntityName GROUP BY uniqueID; 

您将得到一个字典数组,其中每个字典都包含一个uniqueID和一个使用该值的次数。 通过字典运行,并适当地处理重复。

我在博客文章中更详细地描述了这一点。 还有一个来自Apple的示例项目,演示了这个过程,称为SharedCoreData,但我相信它仅作为WWDC 2012示例代码包的一部分提供 。 该会议的会议227中也有介绍。

这似乎是最简单的方法是简单地避免多个作家。 为什么不把驱动程序完全从caching数据中提取出来,然后只从主iOS应用程序更新数据存储?