核心数据 executeFetchRequest 消耗大量内存
Posted
技术标签:
【中文标题】核心数据 executeFetchRequest 消耗大量内存【英文标题】:Core data executeFetchRequest consumes huge amounts of memory 【发布时间】:2013-11-13 11:34:32 【问题描述】:我在核心数据数据库中插入 cca 100 000 条记录。数据库包含 3 个实体。 球员,俱乐部,球员俱乐部 实体处于关系中: 球员>球员俱乐部俱乐部 在插入 PlayerClub 时,我注意到在插入大约 50 000 条记录后会消耗大量内存并降低速度。记录永远不会更新,因为 PlayerClub 只有唯一值。 出于测试原因,我清空了 Player 和 Club 表并再次运行该程序。速度没有下降,但内存消耗再次巨大。这是代码:
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 方法基本相同,所以我不发布它。 这段代码被调用了大约 100 000 次。之前的内存消耗约为 150 MB。在它完成它的 650 MB 之后。所以它消耗 500 MB,没有保存上下文,也没有从数据库中获取任何内容,因为表是空的。如果我评论
arTmp = [context executeFetchRequest:request error:&error];
在 DohvatiIgracZaIgracId 和 DohvatiKlubZaKlubId 方法中,内存消耗降至 200 MB。因此,对于不执行任何操作的代码行,差异为 400MB。这不可能。任何人都有一些想法。一段时间后,我的应用消耗了超过 2.5 GB。
mesuments 是在模拟器上完成的,因为我需要构建一个稍后会预加载的数据库,所以速度很重要 :)... 提前谢谢
【问题讨论】:
您可能应该研究一下 CoreData 的批处理。 但是我的 fetch 限制设置为 1,为什么我需要使用批处理? 好的,我在两个请求上都将 setFetchBatchSize 设置为 1,O 赢得了 50 MB。无法解释为什么,但我做到了,但仍然 450 太多了:) 您可能遇到了保留周期问题。 breaking retain cycles 可能是,但有所作为的行什么也没获取,0 个对象,只是被调用了很多时间 【参考方案1】:不保存上下文
这是有经验的 Core Data 开发人员说“哦,天哪”的问题的一部分。那是你最大的问题。定期保存更改 - 每 50 个条目或每 100 个条目,但无论您做什么,不要等到完成。您正在强制 Core Data 将所有这些对象作为未保存的更改保留在内存中。这是您遇到内存问题的最大原因。
您应该考虑的其他一些事项:
不要一次获取一个对象。获取相对昂贵。如果您运行 100k 个实例并一次获取每个实例,那么您的代码将花费几乎所有时间来执行获取。分批获取 50-100 个(您可以调整数量以获得速度与内存使用的最佳平衡)。处理一个批次,然后在批次结束时保存更改。
当你完成了一个获取的对象时,告诉托管对象上下文你已经完成了。通过使用NO
作为第二个参数调用refreshObject:mergeChanges:
来执行此操作。这告诉上下文它可以释放它用于对象的任何内部内存。这会丢失对象上所有未保存的更改,但如果您没有进行任何更改,则不会丢失任何内容。
考虑完全摆脱PlayerClub
实体。 Core Data 支持多对多关系。这种实体几乎从来没有用过。您使用的是 Core Data,而不是 SQL,因此不要像以前那样设计实体类型。
【讨论】:
不错的答案。结合此注释:“您使用的是 Core Data,而不是 SQL,因此不要像以前那样设计实体类型。”我认为重要的是要补充一点,Core Data 不是关系数据库。它是一个对象图。 对。它使用 SQLite,但这是一个实现细节。 Core Data 的 API 与 SQLite 非常不同。 我保存每 100 个条目(在我的原始代码中)。问题是我必须在解析过程中建立关系。这就是我所做的: 我下载所有球员,然后是所有俱乐部,然后我下载连接表,并在保存期间从俱乐部和球员中获取对象以在 PlayerClub 中建立关系。每 100 个 PlayerClub 条目我保存上下文。有没有其他方法可以将 Players 和 Clubs 的对象连接到 PlayerClub 关系?我的意思是我可以在 PlayerClub 完全保存后稍后再做,但这看起来像是额外的工作,我正在努力加快这项工作:) 我需要 PlayerClub,因为它包含一些我无法放入俱乐部或球员的数据,例如球员在俱乐部的位置、他的状态等等......以上是关于核心数据 executeFetchRequest 消耗大量内存的主要内容,如果未能解决你的问题,请参考以下文章
核心数据 executeFetchRequest 返回 NSManagedObject
核心数据 executeFetchRequest 因 exc_bad_access 而失败
核心数据 fetchRequest 给出 executeFetchRequest:error: <null> is not a valid NSFetchRequest
NSPredicate,executeFetchRequest 崩溃