Ios 如何保证主机应用程序和扩展程序使用的共享应用程序容器中的核心数据存储中的唯一条目? 为了有效地提出我的问题,让我们先考虑一下我面临的具体情况:

Ios 如何保证主机应用程序和扩展程序使用的共享应用程序容器中的核心数据存储中的唯一条目? 为了有效地提出我的问题,让我们先考虑一下我面临的具体情况:,ios,sqlite,core-data,watchkit,ios-extensions,Ios,Sqlite,Core Data,Watchkit,Ios Extensions,常规设置 主机iOS 8应用程序 与主机应用捆绑的一个或多个iOS 8扩展(WatchKit、Share等) 主机应用程序和所有扩展程序在共享应用程序组容器中共享同一核心数据SQLite存储 每个应用程序/扩展都有自己的NSPersistentStoreCoordinator和NSManagedObjectContext 每个持久存储协调器使用一个持久存储,该持久存储与所有其他持久存储在组容器中共享相同的SQLite资源 该应用程序和所有扩展都使用一个公共代码库来同步来自Internet上远程

常规设置

  • 主机iOS 8应用程序
  • 与主机应用捆绑的一个或多个iOS 8扩展(WatchKit、Share等)
  • 主机应用程序和所有扩展程序在共享应用程序组容器中共享同一核心数据SQLite存储
  • 每个应用程序/扩展都有自己的NSPersistentStoreCoordinator和NSManagedObjectContext
  • 每个持久存储协调器使用一个持久存储,该持久存储与所有其他持久存储在组容器中共享相同的SQLite资源
  • 该应用程序和所有扩展都使用一个公共代码库来同步来自Internet上远程API资源的内容
导致问题的事件顺序

  • 用户启动主机应用程序。它开始从远程API资源获取数据。核心数据模型对象基于API响应创建,并“升级”到主机应用程序的托管对象上下文中。每个API实体都有一个在远程API后端标识它的唯一ID。所谓“upsert”,我的意思是,对于每个API实体,如果找不到给定uniqueID的现有条目,主机应用程序只会在核心数据中创建一个新条目

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

  • 问题:如果主机应用程序和扩展程序同时尝试为同一API实体插入核心数据条目,会发生什么情况?为了了解这是如何发生的,让我们看一下upsert的事件顺序:

  • 核心数据上传顺序:

  • API解析代码解析给定API实体的uniqueID
  • 解析器对与谓词匹配的任何条目执行核心数据提取,其中
    uniqueID
    等于解析的uniqueID
  • 如果未找到现有条目,解析器将为此API实体插入一个新的核心数据条目,并将其
    uniqueID
    属性设置为已解析的uniqueID
  • 解析器保存托管对象上下文,将新条目数据向下推送到SQLite备份存储区
  • 问题细节

    假设主机应用程序和扩展程序同时独立解析同一API实体的API响应。如果主机应用程序和扩展程序在任何一个完成步骤4之前都达到步骤3,那么它们都将尝试为同一个uniqueID插入新的核心数据条目。当它们到达步骤4并在各自的托管对象上下文上调用
    save:
    时,核心数据将愉快地创建重复条目

    据我所知,核心数据无法将属性标记为唯一的。我需要一个核心数据相当于一个。或者我需要一种方法来“锁定”持久性存储的SQLite备份存储,这听起来像是一个麻烦的配方


    对于iOS 8扩展引入的这一相当新颖的问题,是否有一种已知的解决方法?

    解决这一问题的最简单方法似乎是首先避免多个编写器。为什么不直接将您的扩展完全从缓存数据中删除,然后只从主iOS应用程序更新您的数据存储

    对于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
    ,以及使用该值的次数计数。通读字典,适当地处理重复的内容


    我在中更详细地描述了这一点。苹果公司也有一个示例项目演示了这个过程,名为SharedCodeData,但我相信它只是这个过程的一部分。在那次会议的第227次会议中也对其进行了描述。

    为什么在主机进入后台模式时不取消/暂停GET请求?我在核心数据哑时也得到了重复的条目,在核心数据Upsert序列的第2步中:如果有多个结果,我会删除额外的插入。这是推荐的方法。