dispatch_group 中的内存泄漏
Memory Leak in a dispatch_group
我的应用程序在执行具体操作后消耗的(大量)内存量有问题。基本上,这是一组大约 80 HTTP
个请求 (*),我必须在数据同步操作中等待所有请求完成。一切都在 NSOperation 中,它由 NSOperationQueue 调用。我收到请求,解析 JSON,并将结果保存在核心数据中,没有什么奇怪的。伪代码如下:
NSArray *idsToFetch = ...;
NSString *serverRequestFilter = ...;
//Suppose there are 80 batches, so here is one of them:
serverRequestFilter = [NSString stringWithFormat:---];
NSURL *url = [NSURL URLWithString:urlString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
// <Setup some headers...>
NSURLSession *session = [NSURLSession sharedSession];
dispatch_group_enter(group);
[[session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
// <Some error handling here>
id res = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL];
if ([res isKindOfClass:[NSDictionary class]]) {
NSDictionary *d = [(NSDictionary *)res objectForKey:@"d"];
NSArray *results = [d objectForKey:@"results"];
d = nil;
res = nil;
// <...>
// Save in Core Data
while (i < count) {
[moc performBlockAndWait:^{
Blablabla *bp;
bp = (Blablabla *)[NSEntityDescription insertNewObjectForEntityForName:@"Blablabla" inManagedObjectContext:moc];
NSDictionary *rel = results[i];
[bp setXXX:[rel valueForKey:@"XXX"]];
// <the same for about 10 attributes>
}
}
// <Core data save>
dispatch_group_leave(group);
}
}] resume];
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
此时内存消耗可高达120Mb。我可以继续使用该应用程序,将其置于后台或其他任何方式,我必须有内存泄漏的母亲。 如果我离开这个视图控制器,内存消耗会一直很高。
我分析了该应用程序,看到大量内存泄漏到字符串中 (?),但我不知道如何修复它。
在配置文件中挖掘,来自该字符串的顶部 'responsible callers' 是:
- [NSPlaceholderString initWithFormat:locale:arguments:] (15k+)
- [NSSQLCore _prepareDictionaryResultsFromResultSet:usingFetchPlan:] (15k+)
- [NSURL(NSURL) initWithString:relativeToURL:] (the number of HTTP requests)
提前致谢
(*) 由于服务器限制,我必须这样做。我必须获取很多实体,所以我使用分页来完成。
在使用块和内存管理时,请考虑以下几点:
- 使用自动释放池优化内存以应对繁重的循环
- 尝试最小化 CoreData 执行块,就好像我们在 Core Data 后面使用 SQLite 持久存储一样,每个执行块都作为一个事务工作并且非常昂贵。
尝试使用class级实例变量的弱引用
dispatch_group_enter(group);
[[session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
// <Some error handling here>
id res = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL];
if ([res isKindOfClass:[NSDictionary class]]) {
NSDictionary *d = [(NSDictionary *)res objectForKey:@"d"];
NSArray *results = [d objectForKey:@"results"];
d = nil;
res = nil;
// <...>
// Save in Core Data
// CHANGE: optimize memory with autorelease pool
@autoreleasepool{
__block NSInteger localCount = count;
[moc performBlockAndWait:^{
// CHANGE: add while loop inside performBlock, because, perform block in loop on managedobjectcontext is quite costly
while (i < localCount) {
Blablabla *bp;
bp = (Blablabla *)[NSEntityDescription insertNewObjectForEntityForName:@"Blablabla" inManagedObjectContext:moc];
NSDictionary *rel = results[i];
[bp setXXX:[rel valueForKey:@"XXX"]];
// <the same for about 10 attributes>
}
}
// <Core data save>
}
dispatch_group_leave(group);
}
}] resume];
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
我的应用程序在执行具体操作后消耗的(大量)内存量有问题。基本上,这是一组大约 80 HTTP
个请求 (*),我必须在数据同步操作中等待所有请求完成。一切都在 NSOperation 中,它由 NSOperationQueue 调用。我收到请求,解析 JSON,并将结果保存在核心数据中,没有什么奇怪的。伪代码如下:
NSArray *idsToFetch = ...;
NSString *serverRequestFilter = ...;
//Suppose there are 80 batches, so here is one of them:
serverRequestFilter = [NSString stringWithFormat:---];
NSURL *url = [NSURL URLWithString:urlString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
// <Setup some headers...>
NSURLSession *session = [NSURLSession sharedSession];
dispatch_group_enter(group);
[[session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
// <Some error handling here>
id res = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL];
if ([res isKindOfClass:[NSDictionary class]]) {
NSDictionary *d = [(NSDictionary *)res objectForKey:@"d"];
NSArray *results = [d objectForKey:@"results"];
d = nil;
res = nil;
// <...>
// Save in Core Data
while (i < count) {
[moc performBlockAndWait:^{
Blablabla *bp;
bp = (Blablabla *)[NSEntityDescription insertNewObjectForEntityForName:@"Blablabla" inManagedObjectContext:moc];
NSDictionary *rel = results[i];
[bp setXXX:[rel valueForKey:@"XXX"]];
// <the same for about 10 attributes>
}
}
// <Core data save>
dispatch_group_leave(group);
}
}] resume];
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
此时内存消耗可高达120Mb。我可以继续使用该应用程序,将其置于后台或其他任何方式,我必须有内存泄漏的母亲。 如果我离开这个视图控制器,内存消耗会一直很高。
我分析了该应用程序,看到大量内存泄漏到字符串中 (?),但我不知道如何修复它。
在配置文件中挖掘,来自该字符串的顶部 'responsible callers' 是:
- [NSPlaceholderString initWithFormat:locale:arguments:] (15k+)
- [NSSQLCore _prepareDictionaryResultsFromResultSet:usingFetchPlan:] (15k+)
- [NSURL(NSURL) initWithString:relativeToURL:] (the number of HTTP requests)
提前致谢
(*) 由于服务器限制,我必须这样做。我必须获取很多实体,所以我使用分页来完成。
在使用块和内存管理时,请考虑以下几点:
- 使用自动释放池优化内存以应对繁重的循环
- 尝试最小化 CoreData 执行块,就好像我们在 Core Data 后面使用 SQLite 持久存储一样,每个执行块都作为一个事务工作并且非常昂贵。
尝试使用class级实例变量的弱引用
dispatch_group_enter(group); [[session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { // <Some error handling here> id res = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL]; if ([res isKindOfClass:[NSDictionary class]]) { NSDictionary *d = [(NSDictionary *)res objectForKey:@"d"]; NSArray *results = [d objectForKey:@"results"]; d = nil; res = nil; // <...> // Save in Core Data // CHANGE: optimize memory with autorelease pool @autoreleasepool{ __block NSInteger localCount = count; [moc performBlockAndWait:^{ // CHANGE: add while loop inside performBlock, because, perform block in loop on managedobjectcontext is quite costly while (i < localCount) { Blablabla *bp; bp = (Blablabla *)[NSEntityDescription insertNewObjectForEntityForName:@"Blablabla" inManagedObjectContext:moc]; NSDictionary *rel = results[i]; [bp setXXX:[rel valueForKey:@"XXX"]]; // <the same for about 10 attributes> } } // <Core data save> } dispatch_group_leave(group); } }] resume]; dispatch_group_wait(group, DISPATCH_TIME_FOREVER);