NSMutableRequest 对象主体的分配数据未释放

Posted

技术标签:

【中文标题】NSMutableRequest 对象主体的分配数据未释放【英文标题】:Allocated data of NSMutableRequest object's body not being released 【发布时间】:2013-04-22 15:10:13 【问题描述】:

在我的应用程序中,我必须执行多部分 POST 请求。在请求中,我必须发送一个存储在设备照片库中的文件。完成上传并接收到返回的数据后,我向请求发送了一条释放消息,但 Instruments 显示数据保留在设备的内存中。

这就是我创建请求对象的方式:

NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setCachePolicy:NSURLRequestReloadIgnoringLocalCacheData];

[request setHTTPShouldHandleCookies:YES];
[request setTimeoutInterval:30];
[request setHTTPMethod:@"POST"];
NSString *boundary = @"---------------------------14737809831466499882746641449";

// set Content-Type in HTTP header
NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary];
[request setValue:contentType forHTTPHeaderField: @"Content-Type"];

// post body
NSMutableData *body = [[NSMutableData alloc] init];

// add parts (all parts are strings) `parts` is a NSDictionary object
for(id key in parts) 
    [body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", key] dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:[[NSString stringWithFormat:@"%@\r\n", [parts objectForKey:key]] dataUsingEncoding:NSUTF8StringEncoding]];


// add files data - `file` is a ALAssetRepresentation object
NSData *fileData;
NSString *filename = [file filename];
Byte *buffer = (Byte*)malloc(file.size);
NSUInteger buffered = [file getBytes:buffer fromOffset:0.0 length:file.size error:nil];
fileData = [[NSData alloc] initWithBytesNoCopy:buffer length:buffered freeWhenDone:YES];




if (fileData) 
    [body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];

    if ([requestType isEqualToString:PUBLISH_TYPE_VC_MANDA]) 
        [body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"attachment\"; filename=\"%@\"\r\n", filename] dataUsingEncoding:NSUTF8StringEncoding]];
        [body appendData:[@"Content-Type: image/jpeg\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
     else 
        [body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"file\"; filename=\"%@\"\r\n", filename] dataUsingEncoding:NSUTF8StringEncoding]];
        [body appendData:[@"Content-Type: application/octet-stream\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
    
    [body appendData:fileData];
    [body appendData:[[NSString stringWithFormat:@"\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];


[fileData release];


[body appendData:[[NSString stringWithFormat:@"--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];


// setting the body of the post to the reqeust
[request setHTTPBody:body];

[body release];

// set the content-length
NSString *postLength = [NSString stringWithFormat:@"%d", [body length]];
[request setValue:postLength forHTTPHeaderField:@"Content-Length"];
[request setValue:@"Keep-Alive" forHTTPHeaderField:@"Connection"];

// set URL `adress` is a NSURL object
[request setURL:address];

//set cookie
if (storedCookie) 

    NSDictionary *properties = [NSDictionary dictionaryWithObjectsAndKeys:[storedCookie name], NSHTTPCookieName, myDomain, NSHTTPCookieDomain,@"/",NSHTTPCookiePath, [storedCookie value], NSHTTPCookieValue, nil];

    NSHTTPCookie *cookie = [NSHTTPCookie cookieWithProperties:properties];
    NSArray* cookies = [NSArray arrayWithObjects: cookie, nil];
    NSDictionary * headers = [NSHTTPCookie requestHeaderFieldsWithCookies:cookies];
    [request setAllHTTPHeaderFields:headers];

在此之后,收到响应后,我执行[request release],但分配的字节保留在内存中。

我在分配模式下运行Instruments,它声称有一个类别为Malloc 91MB 的对象(我正在上传的ALAsset 的大小)。通过查看调用树,占用所有内存的符号是上述方法中的[NSConcreteMutableData appendBytes:length:]

为什么不释放字节?更重要的是,为什么它必须将数据移动到设备的内存中?我见过像 Facebook 这样的应用程序从图库上传非常大的视频而没有任何内存问题。

非常感谢您的关注,任何帮助或指导将不胜感激

编辑: 进一步调查显示,当我使用 [request release] 时,请求对象被释放,但它的主体没有。很奇怪的行为......

EDIT2:我明确使用了[request.HTTPBody release];,并且释放了字节。我仍然无法相信[request release] 没有释放它的身体。这可能是一个错误吗?

【问题讨论】:

您不使用 ARC 有什么原因吗? 我个人很想用ARC,但是店主规定我们不能用它 为什么?兼容性不再是一个很好的理由。 【参考方案1】:

您的请求的 retainCount 为 2:

NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init]; // 1

...

[request retain]; // 2

那么如果你做[request release],它就会减一。

解决办法是不做[request retain]

【讨论】:

我完全同意你的看法。我删除了保留,但仍然没有释放内存。有什么想法吗? 你也忘了调用free(buffer); Apple 文档说 freeWhenDone:YES 应该注意这一点 没问题我的朋友,我真的非常感谢你的帮助。查看我在问题中的编辑,我知道发生了什么 注意绝对的retainCount是不相关的;它可能是也可能不是@kovpass 声称的(并且可能不会在请求中使用 URL 后立即出现)。关键是您必须在释放对象之前平衡保留与释放【参考方案2】:

经过进一步调查,内存没有被释放,因为需要手动释放请求的属性HTTPBody(参见我的第二次编辑)。

至于在NSRequest 中发送大块数据,有一种更简单的方法不会消耗设备的内存。您必须将文件写入磁盘,然后对其进行映射。我已经在this SO answer 中详细描述了它。希望它可以帮助某人

【讨论】:

以上是关于NSMutableRequest 对象主体的分配数据未释放的主要内容,如果未能解决你的问题,请参考以下文章

无法使用类型的参数列表调用“dataTask”(使用:NSMutableRequest,...)

NSMutableRequest 和 NSURLConnection 的最大超时时间

对 NSMutableRequest 的 PHP Json 请求!帮助?

HTTPBody 中的 NSMutableRequest 数组和字典,服务器响应

在 NSMutableRequest 的正文中发送 JSON 格式的 NSDate

聊聊架构--读书笔记