在IOS中使用Web服务时会发生NSURLSession内存泄漏

我正在构build一个使用Web服务的应用程序,并从该Web服务获取信息,我使用NSURLSessionNSURLSessionDataTask

我现在在内存testing阶段,我发现NSURLSession导致内存泄漏。

这不是所有的泄漏。这是我可以适应的图片。

这不是所有的泄漏。 这是我可以适应的图片。

以下是我如何设置NSURLSession并从Web服务请求信息。

 #pragma mark - Getter Methods - (NSURLSessionConfiguration *)sessionConfiguration { if (_sessionConfiguration == nil) { _sessionConfiguration = [NSURLSessionConfiguration ephemeralSessionConfiguration]; [_sessionConfiguration setHTTPAdditionalHeaders:@{@"Accept": @"application/json"}]; _sessionConfiguration.timeoutIntervalForRequest = 60.0; _sessionConfiguration.timeoutIntervalForResource = 120.0; _sessionConfiguration.HTTPMaximumConnectionsPerHost = 1; } return _sessionConfiguration; } - (NSURLSession *)session { if (_session == nil) { _session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:[NSOperationQueue mainQueue]]; } return _session; } #pragma mark - #pragma mark - Data Task - (void)photoDataTaskWithRequest:(NSURLRequest *)theRequest { #ifdef DEBUG NSLog(@"[GPPhotoRequest] Photo Request Data Task Set"); #endif // Remove existing data, if any if (_photoData) { [self setPhotoData:nil]; } self.photoDataTask = [self.session dataTaskWithRequest:theRequest]; [self.photoDataTask resume]; } #pragma mark - #pragma mark - Session - (void)beginPhotoRequestWithReference:(NSString *)aReference { #ifdef DEBUG NSLog(@"[GPPhotoRequest] Fetching Photo Data..."); #endif _photoReference = aReference; NSString * serviceURLString = [[NSString alloc] initWithFormat:@"%@/json?photoreference=%@", PhotoRequestBaseAPIURL, self.photoReference]; NSString * encodedServiceURLString = [serviceURLString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; serviceURLString = nil; NSURL * serviceURL = [[NSURL alloc] initWithString:encodedServiceURLString]; encodedServiceURLString = nil; NSURLRequest * request = [[NSURLRequest alloc] initWithURL:serviceURL]; [self photoDataTaskWithRequest:request]; serviceURL = nil; request = nil; } - (void)cleanupSession { #ifdef DEBUG NSLog(@"[GPPhotoRequest] Session Cleaned Up"); #endif [self setPhotoData:nil]; [self setPhotoDataTask:nil]; [self setSession:nil]; } - (void)endSessionAndCancelTasks { if (_session) { #ifdef DEBUG NSLog(@"[GPPhotoRequest] Session Ended and Tasks Cancelled"); #endif [self.session invalidateAndCancel]; [self cleanupSession]; } } #pragma mark - #pragma mark - NSURLSession Delegate Methods - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error { #ifdef DEBUG NSLog(@"[GPPhotoRequest] Session Completed"); #endif if (error) { #ifdef DEBUG NSLog(@"[GPPhotoRequest] Photo Request Fetch: %@", [error description]); #endif [self endSessionAndCancelTasks]; switch (error.code) { case NSURLErrorTimedOut: { // Post notification [[NSNotificationCenter defaultCenter] postNotificationName:@"RequestTimedOut" object:self]; } break; case NSURLErrorCancelled: { // Post notification [[NSNotificationCenter defaultCenter] postNotificationName:@"RequestCancelled" object:self]; } break; case NSURLErrorNotConnectedToInternet: { // Post notification [[NSNotificationCenter defaultCenter] postNotificationName:@"NotConnectedToInternet" object:self]; } break; case NSURLErrorNetworkConnectionLost: { // Post notification [[NSNotificationCenter defaultCenter] postNotificationName:@"NetworkConnectionLost" object:self]; } break; default: { } break; } } else { if ([task isEqual:_photoDataTask]) { [self parseData:self.photoData fromTask:task]; } } } - (void)URLSession:(NSURLSession *)session didBecomeInvalidWithError:(NSError *)error { if (error) { #ifdef DEBUG NSLog(@"[GPPhotoRequest] Session Invalidation: %@", [error description]); #endif } if ([session isEqual:_session]) { [self endSessionAndCancelTasks]; } } #pragma mark - #pragma mark - NSURLSessionDataTask Delegate Methods - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data { #ifdef DEBUG NSLog(@"[GPPhotoRequest] Received Data"); #endif if ([dataTask isEqual:_photoDataTask]) { [self.photoData appendData:data]; } } #pragma mark - 

问题:为什么NSURLSession导致这些内存泄漏? 当我完成它时,我正在使NSURLSession无效。 我也释放任何我不需要的属性,并将会话设置为零(请参阅 – (void)cleanupSession& – (void)endSessionAndCancelTask​​s)。

其他信息:会话完成并“清理”后发生内存泄漏。 有时,他们也出现在我popupUIViewController 。 但是,我所有的自定义(GPPhotoRequest和GPSearch)对象和UIViewController正在处理(我已经确定通过添加一个NSLog)。

我尽量不要张贴太多的代码,但我觉得大部分需要被看到。

如果您需要更多信息,请让我知道。 非常感谢帮助。

当我切换到NSURLSession时,我遇到了同样的“漏洞”,内存pipe理问题。 对于我来说,在创build会话并恢复/启动dataTask之后,我从来没有告诉会话清理自己(即释放分配给它的内存)。

 // Ending my request method with only the following line causes memory leaks [dataTask resume]; // Adding this line fixed my memory management issues [session finishTasksAndInvalidate]; 

从文档 :

在最后一个任务完成后,会话进行最后的委托调用后,对委托和callback对象的引用将被打破。

清理我的会话修复了通过仪器报告的内存泄漏。

在重读URL加载系统编程指南后 ,我发现我把NSURLSession属性设置为零。

相反,我需要设置NSURLSession属性为零后,我收到委托消息URLSession:(NSURLSession *)session didBecomeInvalidWithError:(NSError *)error ,这是有道理的。 幸运的是,这是一个小小的错误。

例如

 - (void)URLSession:(NSURLSession *)session didBecomeInvalidWithError:(NSError *)error { if (error) { #ifdef DEBUG NSLog(@"[GPPhotoRequest] Session Invalidation: %@", [error description]); #endif } if ([session isEqual:_session]) { [self cleanupSession]; } } 

有同样的问题。 @乔纳森的回答没有意义 – 我的应用程序仍然泄漏内存。 我发现在URLSession:didBecomeInvalidWithError: delegate方法中将session属性设置为nil会导致问题。 试图深入了解URL加载系统编程指南 。 它说

在使会话无效之后,当所有未完成的任务被取消或完成时,会话向委托发送一个URLSession:didBecomeInvalidWithError:消息。 当该委托方法返回时,该会话将处理其对委托的强烈引用

我将委托方法留空。 但无效的session属性仍然有一个指针,我应该什么时候设置它nil ? 我只是把这个属性设置得很弱

 // .h-file @interface MyClass : NSObject <NSURLSessionDelegate> { __weak NSURLSession *_session; } // .m-file - (NSURLSessionTask *)taskForRequest:(NSURLRequest *)request withCompletionHandler:(void(^)(NSData *,NSURLResponse *,NSError *))handler { if(!_session) [self initSession]; //... } 

该应用程序停止泄漏内存。