可能是NSURLSession或NSMutableURLRequest不释放内存调用循环

时间:2017-11-02 15:15:44

标签: objective-c nsurlconnection nsurlsession nsurl nsmutableurlrequest

我的问题: - NSURLSession不会释放以前的5MB Chunk调用API内存

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

对于示例500MB视频并创建100个块并使用NSURLSession发送,因此调用100次但NSURLSession不会释放以前调用的5MB Chunk的API内存

(1)我创建了5MB Chunk。

(2)使用带有5MB Chunk的NSFileHandle使用OffSet

读取文件

(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);
            }

2 个答案:

答案 0 :(得分:2)

您的问题可能是您的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循环的异步版本;你的任务一次发生一次,没有调度队列的阻塞,并且由于每个调度队列都有自己的自动释放池,你也不会积累自动释放的对象,你的内存使用应该保持合理。

答案 1 :(得分:0)

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

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

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

一旦你有了这个记忆问题,你也可以采用其他方法来提高性能,但是不要超越自己。