ios - IOS中使用web服务时出现NSURLSession内存泄漏

标签 ios memory-leaks nsurlsession

我正在构建一个使用网络服务的应用程序,我使用 NSURLSessionNSURLSessionDataTask 从该网络服务获取信息。

我现在处于内存测试阶段,我发现 NSURLSession 导致内存泄漏。

This is not all of the leaks. It is all I could fit in the picture.

这不是所有的泄漏。这就是我能放在图片中的所有内容。

下面是我如何设置 NSURLSession 并从网络服务请求信息。

#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 无效。我还释放了我不需要的任何属性并将 session 设置为 nil(请参阅 - (void)cleanupSession & - (void) endSessionAndCancelTask​​s)。

其他信息: 内存泄漏发生在 session 完成并“清理”之后。有时,它们也会在我弹出 UIViewController 之后发生。但是,我所有的自定义(GPPhotoRequest 和 GPSearch)对象和 UIViewController 都被释放(我通过添加 NSLog 来确保)。

我尽量不发布太多代码,但我觉得大部分代码都需要查看。

如果您需要更多信息,请告诉我。非常感谢您的帮助。

最佳答案

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

// Ending my request method with only the following line causes memory leaks
[dataTask resume];

// Adding this line fixed my memory management issues
[session finishTasksAndInvalidate];

来自docs :

After the last task finishes and the session makes the last delegate call, references to the delegate and callback objects are broken.

清理我的 session 修复了通过 Instruments 报告的内存泄漏。

关于ios - IOS中使用web服务时出现NSURLSession内存泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21554987/

相关文章:

ios - 在完成处理程序中的 http 状态代码上隐藏进度条 - Swift 4

当我尝试运行 6 时,iOS 模拟器恢复到 iOS7

javascript - Phonegap,Cordova - 插件问题

ios - 如何通过 FaSTLane 脚本添加 Xcode 本地化语言

c++ - 这会导致内存泄漏吗/我应该如何构建代码

c - 静态动态内存是否会在程序结束时自动释放?

c++ - 内存泄漏——这怎么可能?

iOS 13后台下载问题

ios - NSURLSession.sharedSession 运行时显示旧数据?

iPhone : Hiding/showing Toolbar