问题描述
前不久我在开发公司的一款SDK的过程中,遇到过发生在AFNetworking
使用multipart form request来上传文件的内存泄漏问题,调试了半天,后来终于发现问题在于用AFHTTPRequestSerializer
的multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock:error:
方法来构建multipart form request,会产生内存泄漏。基本上泄露内存的大小,跟上传文件的大小差不多,我没有深入AFHTTPRequestSerializer
的源码,具体原因我不清楚,Google也没搜到类似的情况。
-
说明: 截图中的类名都以
AS
开头,因为这是SDK开发,我直接将AFNetworking
的源码放在了SDK项目里面,然后给AFNetworking
的类前缀加上了AS
从而变成了ASAFNetworking
,这样是为了避免集成了SDK的app同时也在用AFNetworking
,从而产生类冲突。后期会考虑砍掉AFNetworking
,自己基于NSURLSession
写一套HTTP框架。
解决方案
之后我把用AFHTTPRequestSerializer
构造multipart form request的代码,用原生的方式取代,内存泄露问题解决。整个构造multipart form request的代码用了Stack Overflow上搜的一段代码,代码如下:
// configure the request
NSURL *url = @"https://api-url";
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
[request setHTTPMethod:@"POST"];
NSString *boundary = [self generateBoundaryString];
// set content type
NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary];
[request setValue:contentType forHTTPHeaderField: @"Content-Type"];
// create body
NSString *filePath = @"file-path";
NSData *httpBody = [self createBodyWithBoundary:boundary parameters:nil paths:@[filePath] fieldName:@"file"];
request.HTTPBody = httpBody;
上述代码中用到的两个方法:
- (NSData *)createBodyWithBoundary:(NSString *)boundary
parameters:(NSDictionary *)parameters
paths:(NSArray *)paths
fieldName:(NSString *)fieldName {
NSMutableData *httpBody = [NSMutableData data];
// add params (all params are strings)
[parameters enumerateKeysAndObjectsUsingBlock:^(NSString *parameterKey, NSString *parameterValue, BOOL *stop) {
[httpBody appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[httpBody appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", parameterKey] dataUsingEncoding:NSUTF8StringEncoding]];
[httpBody appendData:[[NSString stringWithFormat:@"%@\r\n", parameterValue] dataUsingEncoding:NSUTF8StringEncoding]];
}];
// add image data
for (NSString *path in paths) {
NSString *filename = [path lastPathComponent];
NSData *data = [NSData dataWithContentsOfFile:path];
NSString *mimetype = [self mimeTypeForPath:path];
[httpBody appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[httpBody appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n", fieldName, filename] dataUsingEncoding:NSUTF8StringEncoding]];
[httpBody appendData:[[NSString stringWithFormat:@"Content-Type: %@\r\n\r\n", mimetype] dataUsingEncoding:NSUTF8StringEncoding]];
[httpBody appendData:data];
[httpBody appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
}
[httpBody appendData:[[NSString stringWithFormat:@"--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
return httpBody;
}
- (NSString *)generateBoundaryString {
return [NSString stringWithFormat:@"Boundary-%@", [[NSUUID UUID] UUIDString]];
}
最后leak工具一片绿...好像哪里不对,怎么就绿了呢😂...
non-leak.png
网友评论