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)

提前致谢

(*) 由于服务器限制,我必须这样做。我必须获取很多实体,所以我使用分页来完成。

在使用块和内存管理时,请考虑以下几点:

  1. 使用自动释放池优化内存以应对繁重的循环
  2. 尝试最小化 CoreData 执行块,就好像我们在 Core Data 后面使用 SQLite 持久存储一样,每个执行块都作为一个事务工作并且非常昂贵。
  3. 尝试使用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);