[英]How can I guarantee unique entries in a Core Data store in a shared app container used by both the host app and an extension?
To ask my question effectively, let's first consider the exact scenario I'm facing: 为了有效地提出我的问题,我们首先考虑一下我面临的确切情况:
General Setup 常规设置
Sequence of Events Leading to the Problem 导致问题的事件顺序
The user launches the host app. 用户启动主机应用程序。 It begins fetching data from the remote API resource. 它开始从远程API资源获取数据。 Core data model objects are created based on the API response and "upserted" into the host app's managed object context. 核心数据模型对象基于API响应创建,并“upserted”到主机应用程序的托管对象上下文中。 Each API entity has a uniqueID that identifies it in the remote API backend. 每个API实体都有一个uniqueID,用于在远程API后端中标识它。 By "upsert," I mean that for each API entity, the host app only creates a new entry in Core Data if an existing entry for a given uniqueID cannot be found. 通过“upsert”,我的意思是,对于每个API实体,主机应用程序仅在无法找到给定唯一ID的现有条目时在Core Data中创建新条目。
Meanwhile, the user also launches one of the host app's extensions. 同时,用户还启动了一个主机应用程序的扩展。 It, too, performs some kind of fetch from the same remote API. 它也可以从同一个远程API执行某种提取。 It also attempts to perform an "upsert" when parsing the API responses. 它还尝试在解析API响应时执行“upsert”。
The Problem: What happens if both the host app and an extension try to upsert a Core Data entry for the same API entity at the same time? 问题:如果主机应用程序和扩展程序同时尝试为同一API实体插入Core Data条目,会发生什么? To see how this could come about, let's look at the sequence of events for an upsert: 要了解这是如何产生的,让我们看看upsert的事件序列:
Core Data Upsert Sequence: 核心数据Upsert序列:
uniqueID
is equal to the parsed uniqueID. 解析器对与谓词匹配的任何条目执行核心数据提取,其中uniqueID
等于解析的唯一ID。 uniqueID
attribute to the parsed uniqueID. 如果未找到现有条目,则解析器会为此API实体插入新的Core Data条目,并将其uniqueID
属性设置为已解析的uniqueID。 Problem in Detail 问题详情
Let's assume the host app and the extension are independently parsing an API response for the same API entity at the same time. 假设主机应用程序和扩展程序同时独立地解析同一API实体的API响应。 If both the host app and an extension reach Step 3 before either of them has finished Step 4, then they will both be trying to insert a new Core Data entry for the same uniqueID. 如果主机应用程序和扩展程序在它们中的任何一个完成步骤4之前到达步骤3,则它们都将尝试为相同的唯一ID插入新的核心数据条目。 When they reach Step 4 and call save:
on their respective managed object contexts, Core Data will happily create duplicate entries. 当他们到达步骤4并在各自的托管对象上下文中调用save:
时,Core Data将很乐意创建重复的条目。
As far as I'm aware, Core Data doesn't have any way to mark an attribute as unique. 据我所知,Core Data没有任何方法可以将属性标记为唯一。 I need a Core Data equivalent to a SQLite INSERT OR IGNORE
+ UPDATE
combo. 我需要一个等同于SQLite INSERT OR IGNORE
+ UPDATE
组合的核心数据。 . 。 Or else I need a way to "lock" the persistent store's SQLite backing store, which sounds like a recipe for trouble. 或者我需要一种方法来“锁定”持久存储的SQLite后备存储,这听起来像是一个麻烦的方法。
Is there a known approach to this rather novel problem introduced by iOS 8 extensions? 是否有一种已知的方法来解决iOS 8扩展引入的这个相当新颖的问题?
It seems like the simplest approach to this would be to simply avoid the multiple writers in the first place. 看起来最简单的方法就是首先简单地避免使用多个编写器。 Why not just drive your extensions entirely off cached data, and then only update your data store from your primary iOS app? 为什么不完全从缓存数据驱动扩展,然后只从主iOS应用程序更新您的数据存储?
Is there a known approach to this rather novel problem introduced by iOS 8 extensions? 是否有一种已知的方法来解决iOS 8扩展引入的这个相当新颖的问题?
Yes, it's the same approach that applies when using iCloud with Core Data: let the duplicates happen, but then go and clean them up. 是的,这与将iCloud与Core Data一起使用时采用的方法相同:让重复发生,然后再进行清理。 Both situations run the risk of creating duplicate entries, and there's no completely reliable way to prevent them. 这两种情况都存在创建重复条目的风险,并且没有完全可靠的方法来阻止它们。 Since you have a uniqueID
key, you're in good shape as far as this is concerned. 由于您拥有uniqueID
键,因此就此而言您处于良好状态。
It would be a lot easier, as Dave DeLong notes, to avoid the problem in the first place. 正如Dave DeLong指出的那样,首先要避免这个问题会容易得多。 If that's impossible, you can deal with it, with some extra work. 如果这是不可能的,你可以通过一些额外的工作来处理它。
Finding duplicates would be something like: 找到重复的东西是这样的:
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];
This is pretty much the Core Data equivalent of something like this in SQL: 这几乎就是SQL中类似这样的核心数据:
SELECT uniqueID, COUNT(uniqueID) FROM MyEntityName GROUP BY uniqueID;
You get an array of dictionaries, each of which contains a uniqueID
and a count of the number of times that value is used. 您将获得一个字典数组,每个字典都包含唯一uniqueID
和使用该值的次数。 Run through the dictionary and deal with duplicates appropriately. 运行字典并适当处理重复项。
I described this in more detail in a blog post . 我在博文中更详细地描述了这一点。 There's also a sample project from Apple that demonstrates the process, called SharedCoreData, but I believe it's only available as part of the WWDC 2012 sample code bundle . 还有一个来自Apple的示例项目,演示了名为SharedCoreData的过程,但我相信它只作为WWDC 2012示例代码包的一部分提供 。 It was also described in session 227 at that conference. 该会议的会议227也对此进行了描述。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.