NSURLSession内存泄漏

时间:2015-05-07 16:37:23

标签: ios macos memory-management memory-leaks nsurlsession

即使在NSURLSession无效,使用Instruments运行配置文件之后,一些名为TubeManager的类(可能是私有),HTTPConnectionCache和HTTPConnectionCacheDictionary在内存中仍然存在。

要重现的代码段:

NSURLSessionConfiguration* config = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession* session = [NSURLSession sessionWithConfiguration:config];
NSURLRequest* request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.google.com"]];
NSURLSessionDataTask* sessionDataTask = [session dataTaskWithRequest:request
                                               completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
{
    [session finishTasksAndInvalidate];
}];
[sessionDataTask resume];

Instruments screenshot

3 个答案:

答案 0 :(得分:0)

那么,问题是什么?你想关闭缓存吗?网络响应通常在NSURLCache缓存在内存和持久存储中。

如果此缓存使用有问题,请相应地更改会话配置的requestCachePolicy。或者更改cachePolicy本身的NSMutableURLRequest。您还可以配置会话配置使用的URLCache的最大大小,以限制RAM使用量以及持久性内存使用量。

即使您关闭了缓存,作为一般规则,API调用会增加内存消耗也不会感到惊讶,因为没有您自己的错误。应用程序在第一次执行某项任务时遇到一些适度的内存消耗并不罕见。但是,当应用运行时,人们不应该在后续迭代中看到相同的增长。在跟踪内存使用情况时,通常建议多次重复任务并查看应用程序是否返回某种稳定状态(这是可取的),或者是否继续增长(这需要进一步调查以确保您的代码不是问题的根源)。但我们很少担心最初的内存消耗,除非它是戏剧性的。

查看您的代码段,没有什么明显的错误。我倾向于怀疑iOS的日常内存消耗。假设问题比一般缓存行为更广泛,如果每次应用程序执行此代码时内存消耗都是显着的和/或持续增长,那么提供更多详细信息,我们可以帮助您进一步诊断。

这是我的内存配置文件在四批100个请求之后的样子;发出内存警告后加上最后一个标志:

enter image description here

(注意,这是一个复合图像,所以我可以在第一批之前,第三批之前,最后一批之后以及我手动发布内存警告之后向你显示内存。我将这些组合在一起以使其更容易看看这四个时间点的总分配是多少。)

答案 1 :(得分:0)

请注意,在iOS 9上,安全框架会分配appx。 4k SSL缓存数据,并在您第一次恢复新NSURLSession对象的任务时将其收费到您的应用程序。 Apple Technical Q&A QA1727告诉我们这个SSL缓存持续10分钟,无论如何,因为它是私有的并且完全由系统管理(因为安全性!)。

在您的代码示例中,每次发出请求时都要创建一个新的NSURLSession对象。但是您只是使用defaultSessionConfiguration并且没有指定可能存在强引用的委托,那么您应该做的只是使用单例[NSURLSession sharedSession]并使用resetWithCompletionHandler来清除非安全性分配。或者,如果要自定义配置,请创建自定义单例。

  
      
  • (NSURLSession *)sharedSession Discussion
  •   
     

对于基本请求,URL会话类提供共享单例   会话对象,为您提供合理的默认行为。通过使用   在共享会话中,您可以使用URL获取URL的内容   只需几行代码。

     

与其他会话类型不同,您不创建共享会话;   你只需要通过调用[NSURLSession sharedSession]来请求它。作为一个   结果,您不提供委托或配置对象。   因此,使用共享会话:

   - You cannot obtain data incrementally as it arrives from the server.
   - You cannot significantly customize the default connection behavior.
   - Your ability to perform authentication is limited.
   - You cannot perform background downloads or uploads while your app is    
     not running.
     

共享会话使用共享NSURLCache,NSHTTPCookieStorage,   和NSURLCredentialStorage对象,使用共享的自定义网络   协议列表(使用registerClass:和unregisterClass :)配置,   并基于默认配置。

     

使用共享会话时,通常应该避免使用   自定义缓存,cookie存储或凭据存储(除非   你已经用NSURLConnection这样做了,因为有一个非常   很有可能你最终会超过这个能力   默认会话,此时您将不得不重写所有这些   以适用于您的自定义URL会话的方式进行自定义。

     

换句话说,如果您正在使用缓存,cookie,   您可能应该是身份验证或自定义网络协议   使用 默认会话而不是 默认会话。

(来自NSURLSession Class Reference ... Italics mine; P)

如果您没有使用Apple提供的sharedSession单身人士,那么您至少应该从Apple获取提示并使用会话属性滚动您自己的单身人士。会话的重点是,它的目的是比一个请求更长寿。尽管文档不清楚,但Apple提供单例,并将其称为“会话”#34;这表明会话对象的寿命要长于单个请求。

是的,你应该在某个时候invalidateAndCancel,但不是在每一个请求之后,即使每个请求都完全转到另一个服务器(这几乎不是这种情况)。如果要打破对特定会话的引用,则只需要使其无效并取消;否则,您只需在会话中调用flushWithCompletionHandlerresetWithCompletionHandler即可刷新会话的堆分配到VM,或重置清除堆和VM存储。 (另请参阅我的回答here。)

答案 2 :(得分:0)

在错误的地方调用的finishTasksAndInvalidate ... completionHandler用于处理与会话无关的响应

这是正确的代码:

NSURLSessionConfiguration* config = [NSURLSessionConfigurationdefaultSessionConfiguration];
NSURLSession* session = [NSURLSession sessionWithConfiguration:config];
NSURLRequest* request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.google.com"]];
NSURLSessionDataTask* sessionDataTask = [session dataTaskWithRequest:request
                                           completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
   // handle response...
}];
[sessionDataTask resume];
[session finishTasksAndInvalidate];