处理Core Data中的重复条目

时间:2013-08-15 21:15:56

标签: ios objective-c xcode core-data

我有一个允许用户保存收藏夹的应用。我使用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是否能够检查添加的对象是否重复。是否有可以处理重复检查的谓词?谢谢!

7 个答案:

答案 0 :(得分:18)

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);

请记住这假设是单线程访问

答案 1 :(得分:4)

  

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

不,核心数据并不关心这一点。

  

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

由于您的对象具有您控制的唯一ID,因此请使用该ID对现有收藏夹进行提取。像

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

如果您收到任何结果,则表示已存在具有该ID的收藏夹。并且,如果您想要更改它,您可以参考该收藏夹。

您目前的方法很好,如果只有少数收藏,可能会更快。进行抓取会更好地扩展到许多收藏夹。

答案 2 :(得分:4)

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
}

答案 3 :(得分:3)

只是自iOS 9.0以来的更新,您可以使用模型中的“唯一约束”轻松完成。但请注意,如果您的商店已包含重复的核心数据,则会在应用程序发布时自动迁移失败。

请参见此处 - core data unique constraints

答案 4 :(得分:1)

如果您正在处理多个记录,则迭代计数提取或检索实际对象在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找到唯一值。

答案 5 :(得分:0)

http://dorianroy.com/blog/2015/09/how-to-implement-unique-constraints-in-core-data-with-ios-9/

查看链接。它可从ios9获得。您可以在约束中设置键以停止重复。博客更详细。

希望它能帮助别人:)

答案 6 :(得分:0)

从@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
      }