向 Core Data 添加唯一对象

Posted

技术标签:

【中文标题】向 Core Data 添加唯一对象【英文标题】:Adding unique objects to Core Data 【发布时间】:2009-08-05 22:34:48 【问题描述】:

我正在开发一个从数据库中获取大量对象的 iPhone 应用程序。我想使用 Core Data 来存储这些,但我的关系有问题。

一个详细信息包含任意数量的 POI(兴趣点)。当我从服务器获取一组 POI 时,它们包含一个详细 ID。为了将 POI 与 Detail 关联(按 ID),我的过程如下: 在 ManagedObjectContext 中查询 detailID。 如果该详细信息存在,请将 poi 添加到其中。 如果没有,请创建详细信息(它还有其他将延迟填充的属性)。

问题在于性能。由于涉及到多个关系,对 Core Data 执行持续查询很慢,以至于添加 150 个 POI 的列表需要一分钟。

在我的旧模型中,在 Core Data(各种 NSDictionary 缓存对象)之前,这个过程非常快(在字典中查找一个键,如果它不存在则创建它)

我的关系不止这一个,但几乎每个人都必须做这个检查(有些是多对多的,而且他们有一个真正的问题)。

有没有人对我可以如何提供帮助有任何建议?我可以执行更少的查询(通过搜索多个不同的 ID),但我不确定这会有多大帮助。

一些代码:

        POI *poi = [NSEntityDescription
                insertNewObjectForEntityForName:@"POI"
                inManagedObjectContext:[(AppDelegate*)[UIApplication sharedApplication].delegate managedObjectContext]];

    poi.POIid = [attributeDict objectForKey:kAttributeID];
    poi.detailId = [attributeDict objectForKey:kAttributeDetailID];
    Detail *detail = [self findDetailForID:poi.POIid];
    if(detail == nil)
    
        detail = [NSEntityDescription
                    insertNewObjectForEntityForName:@"Detail"
                    inManagedObjectContext:[(AppDelegate*)[UIApplication sharedApplication].delegate managedObjectContext]];
        detail.title = poi.POIid;
        detail.subtitle = @"";
        detail.detailType = [attributeDict objectForKey:kAttributeType];
    





-(Detail*)findDetailForID:(NSString*)detailID 
NSManagedObjectContext *moc = [[UIApplication sharedApplication].delegate managedObjectContext];
NSEntityDescription *entityDescription = [NSEntityDescription
                                          entityForName:@"Detail" inManagedObjectContext:moc];
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
[request setEntity:entityDescription];

NSPredicate *predicate = [NSPredicate predicateWithFormat:
                          @"detailid == %@", detailID];
[request setPredicate:predicate];
NSLog(@"%@", [predicate description]);

NSError *error;
NSArray *array = [moc executeFetchRequest:request error:&error];
if (array == nil || [array count] != 1)

        // Deal with error...
    return nil;

return [array objectAtIndex:0];

【问题讨论】:

更详细地解释您的对象,并发布您使用的一些代码。几乎 100% 可能有一种方法可以更快地完成您已经在做的事情。 【参考方案1】:

查看 Norman 在他的回答中链接到的 Xcode Core Data Programming Guide 中标题为“Core Data Performance”的页面上标题为“Batch Faulting”的部分。

仅获取那些 id 为 IN 的 managedObjects 集合(NSSetNSArrayNSDictionary)服务器返回的对象的 id 可能更有效。

NSSet *oids = [[NSSet alloc] initWithObjects:@"oid1", @"oid2", ..., nil];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"oid IN %@", oids];
[oids release];

更新:我将此技巧用于acani usersView 的解决方案。基本上,在下载JSON response of users 之后,iPhone 使用popular open source JSON framework 将响应解析为NSArrayNSDictionary 对象,每个对象代表一个用户。然后,它会生成一个NSArray 的 uid,并在 Core Data 上进行批量提取,以查看它们中的任何一个是否已经存在于 iPhone 上。如果没有,它会插入它。如果是这样,它只更新确实存在的那些updated 属性比来自服务器的属性旧。

【讨论】:

【参考方案2】:

感谢诺曼,让我顺利完成了这一切,他让我走上了正确的道路。我会在这里为其他人发布我的助手类。

基本上,我的助手类会查找某个 ID 是否存在 NSManagedObject,并可以为某个 ID 创建它。这对我来说执行得足够快,在我的 iPhone 上执行 1,000 次查找/创建操作大约需要 2 秒(我还在那里做了一些其他事情,纯粹的查找/创建可能更快)。

它通过缓存所有 NSManagedObjects 的字典并检查该缓存而不是执行新的 NSFetchRequest 来做到这一点。

一些可以帮助进一步加快速度的修改: 1. 仅获取 NSManagedObjects 的选定属性 2. 仅将 NSManagedObject 的标识符属性获取到字典中,而不是整个对象。 在我的性能测试中,单个查询并不是慢的部分(但只有 1,000 个项目,我希望它会很快)。缓慢的部分是项目的创建。

  #import "CoreDataUniquer.h"


@implementation CoreDataUniquer

    //the identifying property is the field on the NSManagedObject that will be used to look up our custom identifier
-(id)initWithEntityName:(NSString*)newEntityName andIdentifyingProperty:(NSString*)newIdProp

    self = [super init];
    if (self != nil) 
        entityName = [newEntityName retain];
        identifyingProperty = [newIdProp retain];
    
    return self;


-(NSManagedObject*)findObjectForID:(NSString*)identifier

    if(identifier == nil)
    
        return nil;
    
    if(!objectList)
       
        NSManagedObjectContext *moc = [(AppDelegate*)[UIApplication sharedApplication].delegate managedObjectContext];
        NSEntityDescription *entityDescription = [NSEntityDescription
                                                  entityForName:entityName inManagedObjectContext:moc];
        NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
        [request setEntity:entityDescription];

        NSError *error;
        NSArray *array = [moc executeFetchRequest:request error:&error];
        objectList = [[NSMutableDictionary dictionary] retain];
        for (NSManagedObject* p in array) 
            NSString* itemId = [p valueForKey:identifyingProperty];
            [objectList setObject:p forKey:itemId];
        
    
    NSManagedObject* returnedObject = [objectList objectForKey:identifier];
    return returnedObject;

-(NSManagedObject*)createObjectForID:(NSString*)identifier


    NSManagedObject* returnedObject = [NSEntityDescription
                                       insertNewObjectForEntityForName:entityName
                                       inManagedObjectContext:[(AppDelegate*)[UIApplication sharedApplication].delegate managedObjectContext]];
    [returnedObject setValue:identifier forKey:identifyingProperty];
    [objectList setObject:returnedObject forKey:identifier];
    return returnedObject;



- (void) dealloc

    DESTROY(entityName);
    DESTROY(identifyingProperty);
    [super dealloc];


@end

【讨论】:

另外,请确保在您的标识符属性上勾选“索引”选项。 谢谢!请您也发布 .h 文件吗? 您尝试通过检查商店中的所有实体来查找具有属性值的实体。您访问所有这些实体,它将故障状态更改为实际查询。我认为你必须对 NSFetchRequest 使用谓词和限制并执行查询。如果 count > 0,则表示具有属性的实体已存在于持久存储中。之后,您可以访问此请求结果以更新您的实体。【参考方案3】:

此页面提供了一些关于优化性能的帮助: http://developer.apple.com/documentation/Cocoa/Conceptual/CoreData/Articles/cdPerformance.html#//apple_ref/doc/uid/TP40003468-SW1

虽然效率不高,但为什么不使用 NSDictionary 在内存中构建它们呢?将 Core Data 中的所有内容读入 NSDictionary,然后合并您的数据,替换 Core Data 中的所有内容。

【讨论】:

有趣的想法。然后,每当创建对象时,模式将是 1. 获取相关对象的可能范围,使用键 2 存储在 NSDictionary 中。如果 id 存在,则将关系添加到该对象 3. 如果 id 不存在,则添加新对象字典和核心数据。添加关系。

以上是关于向 Core Data 添加唯一对象的主要内容,如果未能解决你的问题,请参考以下文章

我应该如何向 Core Data 添加单个项目? insertIntoManagedContext 或 insertNewObjectForEntityForName?

Core Data 多上下文唯一性

手动合并 Core Data 对象

iPhone Core Data 对象添加到 NSMutableArray

NSArrayController 子类添加 Core Data 对象需要啥?

Core Data 中线程安全的唯一实体实例