核心数据导入速度很慢
Core Data importation very slow
我正在开发一个需要从 Json 导入不同 table 中的数据的应用程序。我对所有 table 使用通用函数。问题是当 table 有很多记录时,输入非常慢。
解析JSON函数:
-(void)parseJSON:(NSArray *)json entity:(NSString *)entityName{
[json enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSArray *primaryKeyNameJSON = [self getPrimaryKeyNameForEntityInJSON:entityName];
NSArray *primaryKey = [obj objectsForKeys:primaryKeyNameJSON notFoundMarker:@""];
NSArray *primaryKeyName = [self getPrimaryKeyNameForEntity:entityName];
NSDictionary *primaryKeysAndValues = [NSDictionary dictionaryWithObjects:primaryKey forKeys:primaryKeyName];
NSManagedObject *managedObj = [self getManagedObjectForEntity:entityName primaryKey:primaryKeysAndValues jsonEntity:entityName json:obj];
NSDictionary *allAttributes = [[managedObj entity] attributesByName];
NSArray *attributesKeys = [allAttributes allKeys];
NSDictionary *allRelationships = [[managedObj entity] relationshipsByName];
NSArray *relationshipsKeys = [allRelationships allKeys];
NSArray *keys = [obj allKeys];
for (NSString *jsonKey in keys) {
NSString *dbKey = [self getDBKeyForJSONKey:jsonKey entity:entityName];
if ([attributesKeys containsObject:dbKey]) {
//Attribute
NSAttributeDescription *oneAttribute = [allAttributes objectForKey:dbKey];
if ([oneAttribute attributeType] == NSDateAttributeType){
[managedObj setValue:[[self dateFormatterJSon] dateFromString:[obj objectForKey:jsonKey]] forKey:dbKey];
}else if ([oneAttribute attributeType] == NSInteger32AttributeType || [oneAttribute attributeType] == NSFloatAttributeType){
[managedObj setValue:[[self numberFormatterJSon] numberFromString:[obj objectForKey:jsonKey]] forKey:dbKey];
}else if ([oneAttribute attributeType] == NSStringAttributeType)
[managedObj setValue:[obj objectForKey:jsonKey] forKey:dbKey];
else if ([oneAttribute attributeType] == NSBooleanAttributeType) {
if ([[obj objectForKey:jsonKey] isEqual: @"-1"] || [[obj objectForKey:jsonKey] isEqual: @"1"]){
[managedObj setValue:[NSNumber numberWithBool:YES] forKey:dbKey];
}else{
[managedObj setValue:[NSNumber numberWithBool:NO] forKey:dbKey];
}
}
}
else if ([relationshipsKeys containsObject:dbKey]) {
//Relationship
NSRelationshipDescription *oneRelationship = [allRelationships objectForKey:dbKey];
NSString *destRelationshipEntityName = [[oneRelationship destinationEntity] name];
NSArray *destRelationshipPrimaryKeyName = [self getPrimaryKeyNameForEntity:destRelationshipEntityName];
NSArray *destRelationshipPrimaryKeyValues = [[NSArray alloc] init];
destRelationshipPrimaryKeyValues = [obj objectsForKeys:[self getPrimaryKeyNameForEntity:destRelationshipEntityName inJSONEntity:entityName attrName:jsonKey] notFoundMarker:@""];
NSDictionary *destRelPrimaryKeysAndValues = [NSDictionary dictionaryWithObjects:destRelationshipPrimaryKeyValues forKeys:destRelationshipPrimaryKeyName];
NSManagedObject *destManagedObject = [self getManagedObjectForEntity:destRelationshipEntityName primaryKey:destRelPrimaryKeysAndValues jsonEntity:entityName json:obj];
[managedObj setValue:destManagedObject forKey:dbKey];
}
}];
}
getManagedObjectForEntity函数(这里我们在数据库中查找托管对象,不存在则创建):
-(NSManagedObject *)getManagedObjectForEntity:(NSString *)entityName primaryKey:(NSDictionary *)key jsonEntity:(NSString *)jsonEntity json:(NSDictionary *)json{
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:entityName inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entityDescription];
NSArray *primaryKeyName = [key allKeys];
NSMutableArray *subpredicates = [NSMutableArray array];
NSError *error = nil;
NSArray *managedObjectArray = [[NSArray alloc] init];
for (NSString *k in primaryKeyName) {
NSPredicate *predicate;
NSString *value = [key objectForKey:k];
predicate = [NSPredicate predicateWithFormat:@"(%K == %@)",k,value];
[subpredicates addObject:predicate];
}
NSCompoundPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates:subpredicates];
[request setPredicate:predicate];
NSError *error = nil;
managedObjectArray = [context executeFetchRequest:request error:&error];
if (managedObjectArray == nil)
{
// Deal with error...
}
if ([managedObjectArray count] > 0)
return [managedObjectArray firstObject]; //Object exists
//If object is not found lets create it
NSManagedObject *managedObject = [NSEntityDescription insertNewObjectForEntityForName:entityName inManagedObjectContext:context];
NSDictionary *allAttributes = [entityDescription attributesByName];
NSDictionary *allRelationships = [entityDescription relationshipsByName];
NSArray *allAttributesName = [allAttributes allKeys];
for (NSString *k in primaryKeyName) {
if ([allAttributesName containsObject:k]) {
//Key is an attribute
[managedObject setValue:[key objectForKey:k] forKey:k];
}
else {
//Key is a relationship
NSString *relationshipKey = [[k componentsSeparatedByString:@"."] firstObject];
NSRelationshipDescription *oneRelationship = [allRelationships objectForKey:relationshipKey];
NSString *destEntityName = [[oneRelationship destinationEntity] name];
NSArray *destEntityPrimaryKey = [self getPrimaryKeyNameForEntity:destEntityName];
NSArray *destEntityPrimaryKeyValues = [json objectsForKeys:[self getPrimaryKeyNameForEntity:destEntityName inJSONEntity:jsonEntity attrName:relationshipKey] notFoundMarker:@""];
NSManagedObject *destManagedObj = [self getManagedObjectForEntity:destEntityName primaryKey:[NSDictionary dictionaryWithObjects:destEntityPrimaryKeyValues forKeys:destEntityPrimaryKey] jsonEntity:jsonEntity json:json];
[managedObject setValue:destManagedObj forKey:relationshipKey];
}
}
return managedObject;
}
当所有 JSON 数据都已导入后,我终于使用了 [context save:&error] 功能。
我试过将每个 table 保存在一个数组中并在数组中查找托管对象,但是每个 table 中的大量数据导致内存问题。
有没有更好的方法可以更快地导入所有这些数据?
谢谢
查找和创建模式总是很昂贵。但是,您的设计是为每个 个人 记录访问磁盘。这很浪费,很可能是您速度缓慢的核心。
如果您使用的是 iOS 8 或更早版本,那么您应该使用 KVC 一次获取所有主键并一次获取所有现有记录(或至少获取它们的 NSManagedObjectID
) 然后您可以决定是插入还是更新。
如果您使用的是 iOS 9 或更新版本,则更容易,因为您可以将主键设置为持久存储中的约束(假设您使用的是 SQLite),然后只需 插入所有记录,让Core Data解决插入与更新问题。
至于内存压力,您很可能希望在导入过程中将作业分解并保存几次。每当您保存时,您都可以在上下文中调用 -reset
以要求它从内存中删除其对象。这将帮助您降低压力。
最后,使用 Instruments。虽然我上面给你的一般建议会有所帮助,但你可以使用时间分析器并自己在代码中查找热点来微调导入。
我正在开发一个需要从 Json 导入不同 table 中的数据的应用程序。我对所有 table 使用通用函数。问题是当 table 有很多记录时,输入非常慢。
解析JSON函数:
-(void)parseJSON:(NSArray *)json entity:(NSString *)entityName{
[json enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSArray *primaryKeyNameJSON = [self getPrimaryKeyNameForEntityInJSON:entityName];
NSArray *primaryKey = [obj objectsForKeys:primaryKeyNameJSON notFoundMarker:@""];
NSArray *primaryKeyName = [self getPrimaryKeyNameForEntity:entityName];
NSDictionary *primaryKeysAndValues = [NSDictionary dictionaryWithObjects:primaryKey forKeys:primaryKeyName];
NSManagedObject *managedObj = [self getManagedObjectForEntity:entityName primaryKey:primaryKeysAndValues jsonEntity:entityName json:obj];
NSDictionary *allAttributes = [[managedObj entity] attributesByName];
NSArray *attributesKeys = [allAttributes allKeys];
NSDictionary *allRelationships = [[managedObj entity] relationshipsByName];
NSArray *relationshipsKeys = [allRelationships allKeys];
NSArray *keys = [obj allKeys];
for (NSString *jsonKey in keys) {
NSString *dbKey = [self getDBKeyForJSONKey:jsonKey entity:entityName];
if ([attributesKeys containsObject:dbKey]) {
//Attribute
NSAttributeDescription *oneAttribute = [allAttributes objectForKey:dbKey];
if ([oneAttribute attributeType] == NSDateAttributeType){
[managedObj setValue:[[self dateFormatterJSon] dateFromString:[obj objectForKey:jsonKey]] forKey:dbKey];
}else if ([oneAttribute attributeType] == NSInteger32AttributeType || [oneAttribute attributeType] == NSFloatAttributeType){
[managedObj setValue:[[self numberFormatterJSon] numberFromString:[obj objectForKey:jsonKey]] forKey:dbKey];
}else if ([oneAttribute attributeType] == NSStringAttributeType)
[managedObj setValue:[obj objectForKey:jsonKey] forKey:dbKey];
else if ([oneAttribute attributeType] == NSBooleanAttributeType) {
if ([[obj objectForKey:jsonKey] isEqual: @"-1"] || [[obj objectForKey:jsonKey] isEqual: @"1"]){
[managedObj setValue:[NSNumber numberWithBool:YES] forKey:dbKey];
}else{
[managedObj setValue:[NSNumber numberWithBool:NO] forKey:dbKey];
}
}
}
else if ([relationshipsKeys containsObject:dbKey]) {
//Relationship
NSRelationshipDescription *oneRelationship = [allRelationships objectForKey:dbKey];
NSString *destRelationshipEntityName = [[oneRelationship destinationEntity] name];
NSArray *destRelationshipPrimaryKeyName = [self getPrimaryKeyNameForEntity:destRelationshipEntityName];
NSArray *destRelationshipPrimaryKeyValues = [[NSArray alloc] init];
destRelationshipPrimaryKeyValues = [obj objectsForKeys:[self getPrimaryKeyNameForEntity:destRelationshipEntityName inJSONEntity:entityName attrName:jsonKey] notFoundMarker:@""];
NSDictionary *destRelPrimaryKeysAndValues = [NSDictionary dictionaryWithObjects:destRelationshipPrimaryKeyValues forKeys:destRelationshipPrimaryKeyName];
NSManagedObject *destManagedObject = [self getManagedObjectForEntity:destRelationshipEntityName primaryKey:destRelPrimaryKeysAndValues jsonEntity:entityName json:obj];
[managedObj setValue:destManagedObject forKey:dbKey];
}
}];
}
getManagedObjectForEntity函数(这里我们在数据库中查找托管对象,不存在则创建):
-(NSManagedObject *)getManagedObjectForEntity:(NSString *)entityName primaryKey:(NSDictionary *)key jsonEntity:(NSString *)jsonEntity json:(NSDictionary *)json{
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:entityName inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entityDescription];
NSArray *primaryKeyName = [key allKeys];
NSMutableArray *subpredicates = [NSMutableArray array];
NSError *error = nil;
NSArray *managedObjectArray = [[NSArray alloc] init];
for (NSString *k in primaryKeyName) {
NSPredicate *predicate;
NSString *value = [key objectForKey:k];
predicate = [NSPredicate predicateWithFormat:@"(%K == %@)",k,value];
[subpredicates addObject:predicate];
}
NSCompoundPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates:subpredicates];
[request setPredicate:predicate];
NSError *error = nil;
managedObjectArray = [context executeFetchRequest:request error:&error];
if (managedObjectArray == nil)
{
// Deal with error...
}
if ([managedObjectArray count] > 0)
return [managedObjectArray firstObject]; //Object exists
//If object is not found lets create it
NSManagedObject *managedObject = [NSEntityDescription insertNewObjectForEntityForName:entityName inManagedObjectContext:context];
NSDictionary *allAttributes = [entityDescription attributesByName];
NSDictionary *allRelationships = [entityDescription relationshipsByName];
NSArray *allAttributesName = [allAttributes allKeys];
for (NSString *k in primaryKeyName) {
if ([allAttributesName containsObject:k]) {
//Key is an attribute
[managedObject setValue:[key objectForKey:k] forKey:k];
}
else {
//Key is a relationship
NSString *relationshipKey = [[k componentsSeparatedByString:@"."] firstObject];
NSRelationshipDescription *oneRelationship = [allRelationships objectForKey:relationshipKey];
NSString *destEntityName = [[oneRelationship destinationEntity] name];
NSArray *destEntityPrimaryKey = [self getPrimaryKeyNameForEntity:destEntityName];
NSArray *destEntityPrimaryKeyValues = [json objectsForKeys:[self getPrimaryKeyNameForEntity:destEntityName inJSONEntity:jsonEntity attrName:relationshipKey] notFoundMarker:@""];
NSManagedObject *destManagedObj = [self getManagedObjectForEntity:destEntityName primaryKey:[NSDictionary dictionaryWithObjects:destEntityPrimaryKeyValues forKeys:destEntityPrimaryKey] jsonEntity:jsonEntity json:json];
[managedObject setValue:destManagedObj forKey:relationshipKey];
}
}
return managedObject;
}
当所有 JSON 数据都已导入后,我终于使用了 [context save:&error] 功能。
我试过将每个 table 保存在一个数组中并在数组中查找托管对象,但是每个 table 中的大量数据导致内存问题。 有没有更好的方法可以更快地导入所有这些数据? 谢谢
查找和创建模式总是很昂贵。但是,您的设计是为每个 个人 记录访问磁盘。这很浪费,很可能是您速度缓慢的核心。
如果您使用的是 iOS 8 或更早版本,那么您应该使用 KVC 一次获取所有主键并一次获取所有现有记录(或至少获取它们的 NSManagedObjectID
) 然后您可以决定是插入还是更新。
如果您使用的是 iOS 9 或更新版本,则更容易,因为您可以将主键设置为持久存储中的约束(假设您使用的是 SQLite),然后只需 插入所有记录,让Core Data解决插入与更新问题。
至于内存压力,您很可能希望在导入过程中将作业分解并保存几次。每当您保存时,您都可以在上下文中调用 -reset
以要求它从内存中删除其对象。这将帮助您降低压力。
最后,使用 Instruments。虽然我上面给你的一般建议会有所帮助,但你可以使用时间分析器并自己在代码中查找热点来微调导入。