要有效地提出我的问题,让我们首先考虑我面临的确切情况:
常规设置
导致问题的事件顺序
用户启动主机应用。它开始从远程API资源获取数据。核心数据模型对象基于API响应创建,并“upserted”到主机应用程序的托管对象上下文中。每个API实体都有一个uniqueID,用于在远程API后端中标识它。通过“upsert”,我的意思是,对于每个API实体,如果找不到给定uniqueID的现有条目,则主机应用程序仅在Core Data中创建新条目。
同时,用户还会启动其中一个主机应用程序的扩展程序。它也可以从同一个远程API执行某种提取。它还会在解析API响应时尝试执行“upsert”。
问题:如果主机应用和扩展程序同时尝试为同一个API实体插入核心数据条目,会发生什么?要了解这是怎么发生的,让我们看一下upsert事件的顺序:
核心数据Upsert序列:
uniqueID
等于解析的唯一ID的谓词匹配的任何条目执行核心数据提取。uniqueID
属性设置为已解析的唯一ID。详细问题
假设主机应用程序和扩展程序同时独立地解析同一API实体的API响应。如果主机应用程序和扩展程序在它们中的任何一个完成步骤4之前到达步骤3,则它们都将尝试为相同的唯一ID插入新的核心数据条目。当他们到达第4步并在各自的托管对象上下文中调用save:
时,Core Data将很乐意创建重复的条目。
据我所知,Core Data没有任何方法可以将属性标记为唯一。我需要一个等同于SQLite INSERT OR IGNORE
+ UPDATE
combo.的核心数据。或者我需要一种方法来“锁定”持久存储的SQLite后备存储,这听起来像是一个麻烦的配方。
对于iOS 8扩展引入的这个相当新颖的问题,是否有一种已知的方法?
答案 0 :(得分:10)
对此最简单的方法似乎是首先避免使用多个编写器。为什么不完全从缓存数据驱动扩展,然后只从主iOS应用程序更新数据存储?
答案 1 :(得分:10)
对于iOS 8扩展引入的这个相当新颖的问题,是否有一种已知的方法?
是的,这与将iCloud与核心数据一起使用时采用的方法相同:让重复发生,然后再进行清理。这两种情况都存在创建重复条目的风险,并且没有完全可靠的方法来阻止它们。由于您拥有uniqueID
密钥,因此就此而言,您处于良好状态。
正如Dave DeLong指出的那样,首先要避免这个问题。如果这是不可能的,你可以通过一些额外的工作来处理它。
找到重复的内容就像:
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
和一个使用该值的次数。运行字典并适当处理重复项。
我在a blog post中更详细地描述了这一点。还有一个来自Apple的示例项目演示了这个名为SharedCoreData的过程,但我相信它只能作为WWDC 2012 sample code bundle的一部分使用。该会议的会议227也对此进行了描述。