【问题标题】:Core Data importation very slow核心数据导入很慢
【发布时间】:2016-07-12 10:06:01
【问题描述】:

我正在开发一个需要从 Json 导入不同表中的数据的应用程序。我对所有表都使用通用函数。问题是当表有很多记录时,输入非常慢。

parseJSON 函数:

-(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] 函数。

我尝试将每个表保存在一个数组中并在数组中查找托管对象,但是每个表中的大量数据会导致内存问题。 有没有更好的方法可以更快地导入所有这些数据? 谢谢

【问题讨论】:

    标签: ios objective-c json core-data nsmanagedobject


    【解决方案1】:

    查找和创建模式总是很昂贵。但是,您的设计正在为每个 individual 记录敲击磁盘。这很浪费,很可能是你慢的核心。

    如果您使用的是 iOS 8 或更早版本,那么您应该使用 KVC 一次获取所有主键并一次获取所有现有记录(或至少它们的 NSManagedObjectID)然后您可以做出决定进行插入或更新。

    如果您使用的是 iOS 9 或更新版本,则更容易,因为您可以将主键设置为持久存储中的约束(假设您使用的是 SQLite),然后只需 插入 所有记录并让 Core Data 解决插入与更新问题。

    至于内存压力,您很可能希望在导入期间分解作业并保存几次。每当您保存时,您都可以在上下文中调用 -reset 以要求它从内存中删除其对象。这将帮助您减轻压力。

    最后,使用仪器。虽然我在上面给您的一般建议会有所帮助,但您可以通过使用时间分析器并自己查找代码中的热点来微调导入。

    【讨论】:

    • 感谢您的回答。是的,我使用的是 IOS 9 和 SQLite。你知道是否有任何选项可以将所有主键设置为来自 xcode 的持久存储中的约束?
    • 是的,在数据​​模型中,您可以对每个实体设置约束以反映您的主键。
    • 我在使用关系作为唯一约束时遇到问题,xcode 无法编译。错误:“cdtool 无法编译”。你知道为什么吗?
    • 关系不能是唯一约束。只能使用属性,也可以使用多个属性。
    • 核心数据应该改进它的特性。解决这个问题的最佳方法是什么?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-10-16
    • 2011-06-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多