为了有效地提出我的问题,让我们首先考虑一下我面临的具体场景:
常规设置
- 一个主机 iOS 8 应用程序。
- 与主机应用程序捆绑在一起的一个或多个 iOS 8 扩展(WatchKit、Share 等)。
- 主机应用程序和所有扩展在共享应用程序组容器中共享相同的 Core Data SQLite 存储。
- 每个应用程序/扩展都有自己的 NSPersistentStoreCoordinator 和 NSManagedObjectContext。
- 每个持久性存储协调器都使用一个持久性存储,该持久性存储与所有其他持久性存储共享组容器中相同的 SQLite 资源。
- 该应用程序和所有扩展使用通用代码库来同步来自 Internet 上的远程 API 资源的内容。
导致问题的事件顺序
用户启动主机应用程序。它开始从远程 API 资源获取数据。核心数据模型对象是根据 API 响应创建的,并“插入”到主机应用程序的托管对象上下文中。每个 API 实体都有一个唯一 ID,用于在远程 API 后端中标识它。我所说的“upsert”是指,对于每个 API 实体,如果无法找到给定 uniqueID 的现有条目,主机应用程序只会在核心数据中创建一个新条目。
同时,用户还启动主机应用程序的扩展之一。它也从同一个远程 API 执行某种类型的获取。在解析 API 响应时,它还会尝试执行“upsert”。
问题:如果主机应用程序和扩展程序尝试同时更新插入同一 API 实体的核心数据条目,会发生什么?要了解这是如何发生的,让我们看一下更新插入的事件顺序:
核心数据更新插入顺序:
- API 解析代码解析给定 API 实体的 uniqueID。
- 解析器对与谓词匹配的任何条目执行核心数据获取,其中
uniqueID
等于解析的uniqueID。
- 如果未找到现有条目,则解析器会为此 API 实体插入一个新的 Core Data 条目,并设置其
uniqueID
解析的 uniqueID 的属性。
- 解析器保存托管对象上下文,将新条目数据推送到 SQLite 后备存储。
问题详情
我们假设主机应用程序和扩展程序同时独立地解析同一 API 实体的 API 响应。如果主机应用程序和扩展程序在其中任何一个完成步骤 4 之前都到达步骤 3,那么它们都将尝试为同一 uniqueID 插入新的核心数据条目。当他们到达第 4 步并致电save:
在各自的托管对象上下文中,Core Data 将很乐意创建重复的条目。
据我所知,核心数据没有任何方法将属性标记为唯一。我需要一个相当于SQLite INSERT OR IGNORE + UPDATE combo. https://stackoverflow.com/a/15277374/1078579。否则我需要一种方法来“锁定”持久存储的 SQLite 后备存储,这听起来像是一个麻烦的根源。
对于 iOS 8 扩展引入的这个相当新颖的问题,是否有已知的方法?
对于 iOS 8 扩展引入的这个相当新颖的问题,是否有已知的方法?
是的,这与将 iCloud 与 Core Data 结合使用时采用的方法相同:让重复发生,但然后去清理它们。这两种情况都有创建重复条目的风险,并且没有完全可靠的方法来防止它们。既然你有一个uniqueID
关键,就这一点而言,你的状态很好。
正如戴夫·德隆所说,从一开始就避免这个问题会容易得多。如果这是不可能的,您可以通过一些额外的工作来处理它。
查找重复项将类似于:
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];
这在 Core Data 中几乎相当于 SQL 中的类似内容:
SELECT uniqueID, COUNT(uniqueID) FROM MyEntityName GROUP BY uniqueID;
你得到一个字典数组,每个字典包含一个uniqueID
以及该值的使用次数。浏览字典并适当处理重复项。
我在中更详细地描述了这一点一篇博文 http://www.atomicbird.com/blog/icloud-complications-part-2。 Apple 还有一个示例项目演示了该过程,称为 SharedCoreData,但我相信它仅作为WWDC 2012 示例代码包 https://developer.apple.com/devcenter/download.action?path=/wwdc_2012/wwdc_2012_sample_code/wwdc_2012_session_code.dmg。该会议第 227 场会议也对此进行了描述。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)