【问题标题】:may be NSURLSession or NSMutableURLRequest does not release memory calling through loop可能是 NSURLSession 或 NSMutableURLRequest 不释放内存调用通过循环
【发布时间】:2018-04-15 04:17:55
【问题描述】:

我的问题:- NSURLSession 没有释放以前调用的 5MB 块的 API 内存

我在 do while 循环中调用 API 来上传 500MB 视频。我必须使用不同的 API 发送每个 5MB 的块,而不是在一个 API 中。

例如 500MB 视频并创建 100 个块并使用 NSURLSession 发送所以调用 100 次,但 NSURLSession 不会释放先前调用的 5MB 块的 API 内存

(1) 我创建了 5MB 块。

(2) 使用 NSFileHandle 读取 File,使用 OffSet 5MB Chunk

(3) 更改所有块的 URL 并调用 api(需要将所有块发送到不同的 URL)

我不想在 NSData 中转换视频 (500MB) 我想通过 API 发送块

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
        //dispatch_group_t group = dispatch_group_create();

        __block NSUInteger counterFailure = 0; // PSM Anks calling blob by url fails 4 time, exit for funtion

        arrBlobIds = [[NSMutableArray alloc]init];

        __block NSInteger intBlockIdCount = 100000; // PSM Anks blobid to assign id to every blob
        __block NSUInteger offset = 0; // PSM Anks offset to start posution to read data

        NSUInteger length = [[[NSFileManager defaultManager] attributesOfItemAtPath:[urlOfGallaryVideo path] error:nil] fileSize]; // PSM anks total lenght of media
        NSUInteger intChunkSize = (5000 * 1024); // PSM anks chunk size

            while (offset < length){

                //dispatch_group_enter(group);

                NSLog(@"offset 1 : %lu",(unsigned long)offset);

                // PSM Anks Creat Chunk from file according to length

                NSUInteger intThisChunkSize = length - offset > intChunkSize ? intChunkSize : length - offset;
                //NSData* chunk = [NSData dataWithBytesNoCopy:(char *)[myBlob bytes] + offset length:intThisChunkSize freeWhenDone:NO];

                __block NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:[urlOfGallaryVideo path]];
                [fileHandle seekToFileOffset:offset];
                __block NSData *dataChunk = [fileHandle readDataOfLength:intThisChunkSize];

                // PSM Anks Convert block id in Base 64 encode

                NSData *dataBlockId = [[NSString stringWithFormat:@"%ld",intBlockIdCount] dataUsingEncoding:NSUTF8StringEncoding];
                NSString *base64BlockId = [dataBlockId base64EncodedStringWithOptions:0];

                NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@&comp=block&blockid=%@",[dictAzureSAS objectForKey:@"uri"],base64BlockId]]];

                [request setHTTPMethod:@"PUT"];
                [request setHTTPBody:dataChunk];
                //[request setValue:[NSString stringWithFormat:@"%lu",(unsigned long)dataChunk.length] forHTTPHeaderField:@"Content-Length"]; // Do not need
                //[request setValue:strVideoMIMEType forHTTPHeaderField:@"Content-Type"]; // Do not need
                [request addValue:@"BlockBlob" forHTTPHeaderField:@"x-ms-blob-type"];

                //NSLog(@"request : %@",request);
                //NSLog(@"dataChunk.length : %lu \n url for blob %@ \n request %@",(unsigned long)dataChunk.length,[NSURL URLWithString:[NSString stringWithFormat:@"%@&comp=block&blockid=%@",[dictAzureSAS objectForKey:@"uri"],base64BlockId]],request);

                NSLog(@"dataChunk.length : %lu",(unsigned long)dataChunk.length);

                NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
                config.URLCache = [[NSURLCache alloc] initWithMemoryCapacity:0 diskCapacity:0 diskPath:nil];
                config.timeoutIntervalForRequest = 20.0;
                config.URLCredentialStorage = nil;
                config.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
                ///NSURLSession *session = [NSURLSession sessionWithConfiguration:config];
                config = nil;

                //NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
                NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];


                NSURLSessionDataTask *dataTaskForUpload = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {


                    NSLog(@"Finished with status code: %li", (long)[(NSHTTPURLResponse *)response statusCode]);
                    NSLog(@"response: %@", response);
                    NSLog(@"error: %@ %@", error,error.description);

                    if(data != nil) // PSM anks Check Data is nil otherwise app crashed
                    {
                        NSMutableArray *jsonList = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
                        NSLog(@"jsonList: %@", jsonList);
                    }

                    dataChunk = nil;
                    fileHandle = nil;
                    if(error == nil)
                    {
                        /*
                        // PSM Anks First Add Then increment
                        [arrBlobIds addObject:base64BlockId];
                        intBlockIdCount++;
                        offset += intThisChunkSize;

                        counterFailure = 0;
                        */

                    }
                    else
                    {
                        /*
                        counterFailure++;
                        offset = intThisChunkSize;

                        if(counterFailure >= 4)
                        {

                            NSLog(@"Enter counter Failure %lu",(unsigned long)counterFailure);
                            counterFailure = 0;
                            [self stopLoader];
                            [CommonAlertViewMsgs cannotConnectServer:self];
                            return ;
                        }
                        */
                    }
                    //dispatch_group_leave(group);
                    dispatch_semaphore_signal(semaphore);
                    [session finishTasksAndInvalidate];

                }];


                [dataTaskForUpload resume];
                dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);


                NSLog(@"offset 2 : %lu",(unsigned long)offset);
            }

【问题讨论】:

  • 除了查尔斯在下面的回答,回复@autoreleasepool,我建议不要为每个请求创建一个新的NSURLSession
  • @Rob 感谢您的回复,您想说将 500MB 文件拆分为 5MB 文件意味着我想创建 100 个 5MB 文件。然后一个一个上传后
  • @Rob 我建议不要为每个请求创建一个新的 NSURLSession。在 do while loop NSURLSession 中为每个请求创建一个新的,对吗?
  • 不,我说的恰恰相反。不要不要在您的while 循环中创建一个新的NSURLSession。在循环之前创建它(也许将它保存在类的某个属性中),然后在循环中简单地使用它。

标签: objective-c nsurlconnection nsurlsession nsurl nsmutableurlrequest


【解决方案1】:

您的问题可能是您的 NSData 对象被放入自动释放池中,直到您的主 dispatch_async 块完成后才会耗尽。您可以通过在 while 循环中添加 @autoreleasepool 来解决当前的问题;即

while (offset < length) @autoreleasepool {

但是,您最后的 dispatch_semaphore_wait 正在阻塞调度队列,这通常是不鼓励的。除了将@autoreleaspool 添加到while 循环之外,我的建议是使用调度组而不是信号量,并在末尾使用dispatch_group_notify 而不是dispatch_group_wait。这将导致您的主 dispatch_async 块完成,这将释放所有累积在其中的自动释放对象,然后您传递给 dispatch_group_notify 的块将在您的所有操作完成后被调用。

编辑:了解更多关于你想要做什么的信息,这里有一个替代方案,它可以一次运行一个进程,同时仍然不会阻塞调度队列:

(伪代码)

- (void)sendRequestWithOffset:length:otherParameters: {
    send the url request {
        do what you do

        if newOffset < length {
            [self sendRequestWithOffset:newOffset length:length otherParameters:whatever];
        } else {
            hooray, we're done
        }
    }
}

这有点像递归调用(但不是真的,因为我们不会累积堆栈帧)。基本上,它是您的 while 循环的异步版本;您的任务一次发生一个,调度队列没有阻塞,并且由于每个调度队列都有自己的自动释放池,因此您也不会积累自动释放的对象,并且您的内存使用应该保持合理。

【讨论】:

  • 嗯,有多种设计方法。如果你需要一个连续的进程,你也可以在不阻塞调度队列的情况下做到这一点;只需执行步骤 A,然后在完成处理程序中调用步骤 B,等等。对于此示例,您可以创建一个将文件句柄作为参数的方法,如果尚未完成,则再次调用该方法。跨度>
  • @CharlesSrstka 感谢您的回复我认为我们不能在 ARC 中使用 @autoreleasepool 我正在使用 Xcode 9。对吗?是的,我知道了您推荐 dispatch_group_wait 再次感谢您的及时响应,但我如何在 ARC 中使用 @autoreleasepool
  • @Rob 感谢您的回复是的,我可以将 dispatch_semaphore_wait 替换为 dispatch_group_notify。我已经评论了 dispatch_group_notify 的代码。 dispatch_semaphore_wait 或 dispatch_group_notify 哪个更好。快速处理?
  • @Rob 我当时想运行一个请求,所以在收到第一个请求后只有一个并发请求。我打电话给另一个请求。 ->我得到它基于文件的上传意味着如果视频是500MB然后创建100个5MB大小的文件然后使用文件上传一个一个发送但是ROB我必须根据服务器要求设置(必要的)内容类型
  • @AnkurPatel 如果您只想一次运行一个请求,我会这样做:1) 摆脱 while 循环,并将其内容移动到一个单独的方法中来获取文件句柄和剩余字节数作为参数。 2) 在 NSURLDataTask 的完成处理程序中,从剩余的字节中减去适当的数量,检查是否为零,如果不是,则再次调用该方法。这可能是您一次运行一个任务的最高效方式,因为您不会阻塞任何调度队列,并且任何自动释放的内存都将得到快速处理。
【解决方案2】:

如果您想在上传过程中尽量减少内存占用,您应该:

  • 按照Charles 的建议尝试@autoreleasepool
  • 重复使用一个NSURLSession,而不是每次在循环中重新创建一个新的NSURLSession;和
  • 使用基于文件的上传任务(例如uploadTaskWithRequest:fromFile:)而不是dataTask

注意,对于基于文件的上传任务,您仍然可以像现在一样配置您的NSURLRequest,但不要设置httpBody,而是将其作为上述方法的fromFile 参数提供。

一旦您解决了这个内存问题,您也可以寻求其他方法来提高性能,但我们不要超越自己。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-04-06
    • 2011-11-14
    • 2012-12-10
    • 1970-01-01
    • 1970-01-01
    • 2020-12-08
    • 2014-12-20
    • 1970-01-01
    相关资源
    最近更新 更多