【问题标题】:Handling duplicate entries in Core Data处理 Core Data 中的重复条目
【发布时间】:2013-08-18 04:37:03
【问题描述】:

我有一个允许用户保存收藏夹的应用。我正在使用 Core Data 将收藏夹存储为托管对象。我已经编写了一些代码来防止存储重复项的可能性,但我想知道是否有更好的方法来做到这一点。每个收藏对象都有一个唯一的 ID 字段。在下面的代码中,我只是循环并检查 ID 字段,如果该值已经存在,则将标志值设置为 true,然后跳出循环。

-(BOOL)addFavorite{
    BOOL entityExists = NO;
    if(context){
        // does this favorite already exist?
        NSArray *allFaves = [AppDataAccess getAllFavorites];
        for(Favorite *f in allFaves){
            if([f.stationIdentifier isEqualToString:stID]){
                entityExists = YES;
                break;
            }
        }
        if(!entityExists){
            NSError *err = nil;
            Favorite *fave = [Favorite insertInManagedObjectContext:context];
            fave.stationRealName = riverGauge.name;
            fave.stationIdentifier = stID;
            fave.stationState = @"WV";
            if(![context save:&err]){
                NSLog(@"ERROR: Could not save context--%@", err);
            }
            return YES;            
        }
    return NO;
}

我想知道 Core Data 是否有能力检查添加的对象是否重复。是否有可以处理重复检查的谓词?谢谢!

【问题讨论】:

    标签: ios objective-c xcode core-data


    【解决方案1】:

    CoreData 本身并没有唯一性。它没有两个条目相同的概念。

    要启用这种行为,您必须自己实现它,方法是执行“插入前搜索”,也就是“创建前获取”。

    NSFetchRequest *fetch = [NSFetchRequest fetchRequestWithEntityName:@"Favorite"];
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"stationIdentifier == %@", stID];
    [fetch setPredicate:predicate];
    YourObject *obj = [ctx executeRequest:fetch];
    
    if(!obj) {
        //not there so create it and save
        obj = [ctx insertNewManagedObjectForEntity:@"Favorite"]; //typed inline, dont know actual method
        obj.stationIdentifier = stID;
        [ctx save];
    }
    
    //use obj... e.g.
    NSLog(@"%@", obj.stationIdentifier);
    

    记住这是假设单线程访问

    【讨论】:

    • 我不明白。您一次插入一个对象,然后将两个对象添加到数据库中?这不是缺乏意识,而是有 13 年的历史,Apple 像往常一样不废话。
    【解决方案2】:

    只是自 iOS 9.0 以来的一次更新,您可以使用模型中的“唯一约束”轻松完成此操作。但请注意 - 如果您的商店已经包含重复项,则核心数据在应用发布时将无法进行任何自动迁移。

    例如,请参见此处 - core data unique constraints

    【讨论】:

    • 如何处理迁移?
    • CloudKit 不支持唯一约束并且在 iOS 14 中被禁止
    【解决方案3】:

    Swift 3

    func isExist(id: Int) -> Bool {
        let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: myEntityName)
        fetchRequest.predicate = NSPredicate(format: "id = %d", argumentArray: id)
    
        let res = try! theContext.fetch(fetchRequest)
        return res.count > 0 ? true : false
    }
    

    【讨论】:

    • 这里的上下文是什么?
    • @amish ,它是“NSManagedObjectContext”的一个实例
    • @Vahid let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext,这就是我定义的上下文。这样对吗?我在let res = try! theContext.fetch(fetchRequest) 线上遇到错误。
    【解决方案4】:

    我想知道 Core Data 是否有能力检查添加的对象是否重复。

    不,Core Data 不在乎。

    是否有可以处理重复检查的谓词?

    由于您的对象具有您控制的唯一 ID,因此请使用该 ID 获取现有收藏夹。类似的东西

    NSFetchRequest *fetch = [NSFetchRequest fetchRequestWithEntityName:@"Favorite"];
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"stationIdentifier == %@", stID];
    [fetch setPredicate:predicate];
    

    如果您得到任何结果,则表明具有该 ID 的收藏夹已经存在。而且,您可以参考该收藏夹,以备不时之需。

    如果只有几个收藏夹,您当前的方法很好,并且可能更快。进行抓取将更好地扩展到许多收藏夹。

    【讨论】:

      【解决方案5】:

      Swift 4 来自@Vahid 的回答

      func isEntityAttributeExist(id: Int, entityName: String) -> Bool {
          let appDelegate = UIApplication.shared.delegate as! AppDelegate
          let managedContext = appDelegate.persistentContainer.viewContext
          let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: entityName)
          fetchRequest.predicate = NSPredicate(format: "id == %@", id)
          let res = try! managedContext.fetch(fetchRequest)
          return res.count > 0 ? true : false
      }
      

      【讨论】:

        【解决方案6】:

        如果您要处理多条记录,则迭代计数获取或检索实际对象在 CPU 上的开销非常大。就我而言,我对所有匹配的记录进行了一次提取,但要求提供一个仅包含 UUID 字符串的字典。节省了大量的 CPU 开销。

        例如,我在核心数据中的每条记录上都有一个 uUID 属性。我在 CloudKit 中有一个相应的 UUID 列为@“UUID”。

          //1. Create a request for the entity type, returning an array of dictionaries  
              NSFetchRequest* request = [NSFetchRequest fetchRequestWithEntityName:@"someEntityName"];
              [request setResultType:NSDictionaryResultType];
              [request setReturnsDistinctResults:YES];
              [request setPropertiesToFetch: @[@"uUID"]];
        
          //2. Create an array of UUID strings of the downloaded objects
              NSMutableArray *UUIDstrings = [NSMutableArray new];
              for (CKRecord *record in ckRecords) {
                [UUIDstrings addObject:record[@"UUID"]];
              }
        
           //3. Create a predicate to find any Core Data objects with the same UUID
              [request setPredicate:[NSPredicate predicateWithFormat:@"uUID in %@", UUIDstrings]];
        
           //4. If there are results from the fetch, do a log and you'll see it's a dictionary. 
              NSArray *deck = [self.MOC executeFetchRequest:request error:nil];
        
              NSLog(@"Logging the result of index 0. Should be a dictionary %@", deck.count > 0 ? [deck objectAtIndex:0] : @"No results");
        
           //5. Then either do an embedded fast enumeration (for xx in xx){for xx in xx} to find a match like         
        
                   if ([(NSString *)record[@"UUID"] isEqualToString:[dict valueForKey:@"uUID"]]) 
                  {do something}
        
           //...Or 6. Use a more linear approach with NSSet
        
            //Harvest the core data strings
              NSMutableArray *coreDataStrings = [NSMutableArray new];
                for (NSDictionary *dict in deck) {
                [coreDataStrings addObject:[dict objectForKey:@"uUID"]];
              }
        
           //Create a set of your downloaded objects
              NSSet *arraySet = [NSSet setWithArray:ckRecords];
        
           //Then use a predicate search - a NOT version of above
             NSArray *final = [[arraySet filteredSetUsingPredicate:[NSPredicate predicateWithFormat:@"NOT(UUID in %@)", coreDataStrings]]allObjects];
        

        字典的控制台日志看起来像这样。只需匹配所需的最少信息量:

        dictionary {
           uUID = "AFACB8CE-B29E-4A03-9284-4BD5F5464";
        }
        

        More here at the developer site 寻找唯一值。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2010-11-07
          • 2010-09-22
          • 1970-01-01
          • 2015-07-14
          • 2018-02-04
          • 2018-05-19
          • 1970-01-01
          相关资源
          最近更新 更多