向Core Data添加唯一对象

时间:2009-08-05 22:34:49

标签: objective-c iphone core-data

我正在研究一款从数据库中获取大量对象的iPhone应用程序。我想使用Core Data存储这些内容,但我的关系存在问题。

详细信息包含任意数量的POI(兴趣点)。当我从服务器获取一组POI时,它们包含一个详细ID。为了将POI与详细信息(按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];
}

3 个答案:

答案 0 :(得分:6)

在Xcode的核心数据编程指南页面上查看标题为“核心数据性能”的部分中的“批处理错误”部分,Norman在其答案中链接了该部分。

仅获取其id为IN的服务器返回的对象ID的集合(NSSetNSArrayNSDictionary)的托管对象可能更有效

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进行批量提取,以查看它们中是否存在任何已存在的数据。如果没有,它会插入它。如果是这样,只有当updated属性早于服务器的{{1}}属性时,才会更新确实存在的属性。

答案 1 :(得分:4)

我已经把这一切都搞好了,感谢Norman,他让我走上了正确的道路。我会在这里为其他人发布我的助手课程。

基本上,如果某个ID存在NSManagedObject,我的助手类会查找,并且可以为某个ID创建它。这对我来说足够快,我的iPhone上有1000次查找/创建操作大约需要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

答案 2 :(得分:3)

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

虽然不是很有效,但为什么不用NSDictionary在内存中构建它们呢?读取从Core Data到NSDictionary的所有内容,然后合并您的数据,替换Core Data中的所有内容。