ios – 递归/迭代NSURLSessionDataTask导致内存泄漏

我的代码中存在内存泄漏问题,我需要快速连续获取许多URL,每个GET都受到前一个GET结果的影响.目的是在响应中查找特定内容.

我发现最简洁的方法是递归地实现它,因为我可以使用相同的方法来识别响应中是否存在所需的值.功能上它非常好用,但它会泄漏内存,如下所述.我也以迭代的方式实现了相同的功能,这也泄漏了内存.

在我看来,似乎NSURLSession API负责泄漏这个内存,它只发生在非常快速连续多次调用时.但是,如果有人能指出我正在犯的任何明显错误,我将不胜感激.

2014年9月14日更新:

更新为添加递归计数器,即使代码未执行无限次,也会显示仍然发生泄漏.还略微整理了实现,重新使用NSURLSession和NSURLSessionConfiguration作为视图控制器中的属性.

示例代码

- (void)performURLCallRecursive {

    recursionLimiter++;

    if (recursionLimiter > 10) {
        [self.session finishTasksAndInvalidate];
        return;
    }

    NSURL * checkURL = [NSURL URLWithString:@"http://www.google.com"];

    __block NSMutableuRLRequest * urlRequest = [[NSMutableuRLRequest alloc] initWithURL:checkURL
                                                                            cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
                                                                        timeoutInterval:0.0f];

    __weak typeof(self) weakSelf = self;

    NSURLSessionDataTask * task = [self.session dataTaskWithRequest:urlRequest
                                                  completionHandler:^(NSData *data,NSURLResponse *response,NSError
*error) {

                                                      Nsstring * body = [[Nsstring alloc] initWithData:data encoding:NSUTF8StringEncoding];
                                                      NSLog(@"Body: %@",body);

                                                      [weakSelf performURLCallRecursive];

                                                  }];

    [task resume];
 }

#pragma mark - Getters

- (NSURLSessionConfiguration *)sessionConfiguration {

    if (!_sessionConfiguration) {

        _sessionConfiguration = [NSURLSessionConfiguration ephemeralSessionConfiguration];

        [_sessionConfiguration setAllowsCellularaccess:NO];
        [_sessionConfiguration setTimeoutIntervalForRequest:10.0f];
        [_sessionConfiguration setTimeoutIntervalForResource:10.0f];

        [_sessionConfiguration setURLCache:[[NSURLCache alloc] initWithMemoryCapacity:0 diskCapacity:0 diskPath:nil]];

    }

    return _sessionConfiguration;
 }

- (NSURLSession *)session {

    if (_session == nil) {

        _session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration
                                                 delegate:[SPRSessionDelegate new]
                                            delegateQueue:nil];

    }

    return _session; 
}

仪器报告的内存泄漏. (注意:这些每次都略有不同,但大部分都包含相同的泄漏,只是或多或少相同的泄漏):

进一步更新:

所以,我实际上迭代地实现了相同的代码,并且仍然发生内存泄漏.在这个例子中,我包含了一个循环限制器,因此它永远不会执行.任何人都可以帮我弄清楚这里到底发生了什么?

- (void)performURLCallIterative
{

    int loopLimiter = 0;

    do {

        NSURLSessionConfiguration * defaultSession = [NSURLSessionConfiguration defaultSessionConfiguration];

        [defaultSession setAllowsCellularaccess:NO];
        [defaultSession setTimeoutIntervalForRequest:10.0f];
        [defaultSession setTimeoutIntervalForResource:10.0f];

        NSURLSession * session = [NSURLSession sessionWithConfiguration:defaultSession
                                                               delegate:self
                                                          delegateQueue:nil];


        NSURL * checkURL = [NSURL URLWithString:@"http://google.com"];

        NSMutableuRLRequest * urlRequest = [[NSMutableuRLRequest alloc] initWithURL:checkURL
                                                                        cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
                                                                    timeoutInterval:0.0f];

        __weak NSURLSession * weakSession = session;

        dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

        NSURLSessionDataTask * task = [session dataTaskWithRequest:urlRequest
                                                 completionHandler:^(NSData *data,NSError *error) {

                                                     Nsstring * body = [[Nsstring alloc] initWithData:data encoding:NSUTF8StringEncoding];
                                                     NSLog(@"Body: %@",body);

                                                     dispatch_semaphore_signal(semaphore);

                                                     [weakSession invalidateAndCancel];

                                                 }];

        [task resume];

        dispatch_semaphore_wait(semaphore,disPATCH_TIME_FOREVER);

        loopLimiter++;

    } while (loopLimiter <= 6);

}

2014年9月14日更新:

对于可能在这里找到自己的方式的任何Google员工,iOS 8上仍然会出现这种情况.就我而言,这是iOS中的一个错误.

解决方法

– 2014年9月12日更新

解决方案:等待iOS8.

– 2014年9月10日更新

哇,这正在成为复杂性的第N个维度:P.我希望你能在这里快速找到休息时间.

我还有一些其他的东西供您尝试.

1)你能确保关闭NSZombies吗?在Xcode中,Product-> Scheme-> Edit Scheme …->启用Zombie Objects(未勾选).

2)也尝试使用cachePolicy:NSURLCacheStorageNotAllowed用于NSMutableuRLRequest.

3)你能看到你是否完成了错误?把它放在你的身体字符串赋值周围……

if (error == nil)
{
    //Enter data->string code here
}

4)你能看到你是否没有获得200的身份?

NSInteger statusCode = [(NSHTTPURLResponse *)response statusCode];

5)很难准确描述项目的设置方式.我有一个NSObject类型类,它包含NSURLSession方法,它与调用它的UIViewController类是分开的.然后,您希望选择的计时器或任何递归方法将从UIViewController调用url会话关联的方法.

– 2014年9月9日更新

你对我的问题是正确的(2).数据任务在完成之前恢复,在数据任务完成后,会话无效.我没有看到它这样做,但它是有道理的.刚刚在我的测试中测试,没有关于[session invalidateAndCancel]的泄漏……

你能检查你的完成处理程序是否执行?也许它没有,并且在新任务启动之前会话永远不会被取消?

我注意到在Instruments Leaks报告中有一些对HTTP Headers的引用,也许如果你没有指定[urlRequest setHTTPMethod:@“GET”]请求缺少一些基本头文件

(我会在找到解决方案后进行编辑,所以这看起来不像讨论).

– 原始2014年9月8日

有趣的问题!我有与NSURLSessions相关的麻烦泄漏.绝对@autoreleasepool {}和其他人是目前为止尝试的好建议……但是!

我担心你要求我们过去的事情可能是这里的罪魁祸首.

首先是几点意见:

1)我不清楚为什么你需要在这里__弱自我.您试图避免的保留周期是什么?也许这在你实际使用的代码中比你的“样本”更清楚.

2)在与该会话关联的数据任务甚至有机会完成之前调用使会话无效的原因是什么,更不用说恢复了.数据任务处于挂起状态,直到恢复.

3)如果你递归地运行这样的方法,那么我认为指定或至少考虑委托队列是至关重要的,否则将其设置为nil会将其认为串行操作队列.委托在完成处理程序完成之前调用时会发生什么,在无限循环中 – 很可能是一个巨大的堆积.

我相信这里的主要问题是你在有机会完成之前开始新的或取消NSURLSessionDataTask.看看sesssionWithConfiguration:

(抱歉不能包含图片,希望在这个答案之后)

https://developer.apple.com/library/ios/documentation/Foundation/Reference/NSURLSession_class/Introduction/Introduction.html#//apple_ref/occ/clm/NSURLSession/sessionWithConfiguration

关键在这里……

Important

The session object keeps a strong reference to the delegate
until your app explicitly invalidates the session. If you do not
invalidate the session by calling the invalidateAndCancel or
resetWithCompletionHandler: method,your app leaks memory.

我的建议是……

//Your code above...
    [task resume];
    [session finishTasksAndInvalidate];
}

从理论上讲,这应该可以防止任何新会话在完成之前启动,根据描述,“…无法在会话中创建新任务,但现有任务会一直持续到完成.在最后一个任务完成后,会话成为最后一个委托调用,对委托和回调对象的引用被破坏……“

我仍然不确定在恢复会话之前使会话无效.

我希望这有帮助.祝好运.

相关文章

当我们远离最新的 iOS 16 更新版本时,我们听到了困扰 Apple...
欧版/美版 特别说一下,美版选错了 可能会永久丧失4G,不过只...
一般在接外包的时候, 通常第三方需要安装你的app进行测...
前言为了让更多的人永远记住12月13日,各大厂都在这一天将应...