向 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 集合(NSSet
、NSArray
、NSDictionary
)服务器返回的对象的 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 将响应解析为NSArray
的NSDictionary
对象,每个对象代表一个用户。然后,它会生成一个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?
iPhone Core Data 对象添加到 NSMutableArray