核心数据executeFetchRequest消耗大量内存

时间:2013-11-13 11:34:32

标签: ios iphone core-data

我在核心数据库中插入cca 100 000条记录。数据库包含3个实体。 球员,俱乐部,球员俱乐部 实体处于关系中: 播放器及LT; - >> PlayerClub<< - >俱乐部 插入PlayerClub时,我注意到在插入大约50 000条记录后会消耗大量内存并降低速度。记录永远不会更新,因为PlayerClub只有唯一值。 出于测试原因,我已经清空了玩家和俱乐部表并再次运行程序。没有速度下降,但内存消耗再次巨大。这是代码:

    NSManagedObjectContext *context = [[NSManagedObjectContext alloc] init];
    [context setPersistentStoreCoordinator:ap.persistentStoreCoordinator];
    [context setUndoManager:nil];

    NSEntityDescription *entity = [NSEntityDescription entityForName:@"PlayerClub"
                                              inManagedObjectContext:context];

    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    [request setEntity:entity];

    NSEntityDescription *entityKlubovi = [NSEntityDescription entityForName:@"Club" inManagedObjectContext:context];
    NSFetchRequest *requestKlubovi = [[NSFetchRequest alloc] init];
    [requestKlubovi setEntity:entityKlubovi];
    [requestKlubovi setFetchLimit:1];

    NSEntityDescription *entityIgraci = [NSEntityDescription entityForName:@"Player" inManagedObjectContext:context];
    NSFetchRequest *requestIgraci = [[NSFetchRequest alloc] init];
    [requestIgraci setFetchLimit:1];
    [requestIgraci setEntity:entityIgraci];

    for (int i=0; i<[parsedKlubIgraci count]; i++) {
        @autoreleasepool {
            KlubIgrac *klubIgrac = [[KlubIgrac alloc] initWithEntity:entity insertIntoManagedObjectContext:context];

            klubIgrac.iD = p.id;

            //... add some othe properties

            variables = [NSDictionary dictionaryWithObject:p.igracID forKey:@"ID"];
            localPredicate = [predicateIgraci predicateWithSubstitutionVariables:variables];
            [requestIgraci setPredicate:localPredicate];
            klubIgrac.igrac = [self DohvatiIgracZaIgracId:p.igracID cntx:context request:requestIgraci predicate:localPredicate];

            variables = [NSDictionary dictionaryWithObject:p.klubID forKey:@"ID"];
            localPredicate = [predicateKlubovi predicateWithSubstitutionVariables:variables];
            [requestKlubovi setPredicate:localPredicate];
            klubIgrac.klub = [self DohvatiKlubZaKlubId:p.klubID cntx:context request:requestKlubovi predicate:localPredicate];
        }

    }
+(Club *)DohvatiKlubZaKlubId:(int)klubid cntx:(NSManagedObjectContext *)context    

    request:(NSFetchRequest *)request predicate:(NSPredicate *)predicate
{
    @autoreleasepool {
        NSError *error;
        NSArray *arTmp;
        arTmp = [context executeFetchRequest:request error:&error];
        Club *klub;
        if(arTmp != nil && [arTmp count]){
            return [arTmp objectAtIndex:0];
        }
    }

}

DohvatiIgracZaIgracId方法与DohvatiKlubZaKlubId的方法基本相同,所以我发布了它。 此代码大约被调用10万次。以前的内存消耗大约是150 MB。完成650 MB之后。因此它消耗500 MB,而不保存上下文并且不从数据库中获取任何内容导致表为空。如果我评论

arTmp = [context executeFetchRequest:request error:&error];
在DohvatiIgracZaIgracId和DohvatiKlubZaKlubId方法中,内存消耗降至200 MB。因此,对于一行无效的代码,差异为400MB。这不可能。任何人都有一些想法。一段时间后,我的应用程序消耗超过2.5 GB。

因为我需要建立一个我稍后会预加载的数据库,所以速度非常重要:)...提前预测

1 个答案:

答案 0 :(得分:11)

  

而不保存上下文

这是经验丰富的核心数据开发人员说“哦,圣洁的废话”的问题的一部分。那是你最大的问题。定期保存更​​改 - 每50个条目,或每100个条目,但无论你做什么,都不要等到你完成。您正在强制Core Data将所有这些对象保留在内存中作为未保存的更改。这是你遇到记忆问题的最大原因。

你应该考虑的其他一些事情:

  • 不要一次提取一个对象。取食相对昂贵。如果你运行100k个实例并一次一个地获取它们,你的代码几乎将花费所有时间来执行提取。批量获取50-100(您可以调整数量以获得速度与内存使用的最佳平衡)。处理一批,然后在批处理结束时保存更改。

  • 完成获取的对象后,告诉托管对象上下文您已完成。通过使用refreshObject:mergeChanges:作为第二个参数调用NO来执行此操作。这告诉上下文它可以释放它用于对象的任何内部存储器。这会丢失对象上未保存的任何更改,但如果您没有进行任何更改,那么就没有任何损失。

  • 考虑彻底摆脱PlayerClub实体。核心数据支持多对多关系。这种实体几乎从来没用过。您使用的是Core Data,而不是SQL,因此不要像设计实体类型那样设计实体类型。