我正在研究一款从数据库中获取大量对象的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];
}
答案 0 :(得分:6)
在Xcode的核心数据编程指南页面上查看标题为“核心数据性能”的部分中的“批处理错误”部分,Norman在其答案中链接了该部分。
仅获取其id为IN
的服务器返回的对象ID的集合(NSSet
,NSArray
,NSDictionary
)的托管对象可能更有效
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进行批量提取,以查看它们中是否存在任何已存在的数据。如果没有,它会插入它。如果是这样,只有当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中的所有内容。