如何使用iPhone SDK下载大文件并避免内存使用问题?

21

我正在使用NSURLConnection类在我的iPhone应用程序中下载大文件,但由于它使用了太多的内存,因此有时会崩溃。我正在按照通常的方式使用NSURLConnection,将接收到的数据追加到一个NSMutableData对象中。

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    [self.fileData appendData:data];
}

然后在我下载完整个文件后,我将其保存到本地临时文件中,并像这样将其作为映射文件读取:

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    // save the downloaded data into a temporary file
    NSString *tempPath = NSTemporaryDirectory();
    NSString *tempFile = [tempPath stringByAppendingPathComponent:@"temp.pdf"];
    [self.fileData writeToFile:tempFile atomically:YES];
    NSData *mappedData = [NSData dataWithContentsOfMappedFile:tempFile];

    NSURL *baseURL = [NSURL URLWithString:@"http://mydomain.com"];
    [webView loadData:mappedData MIMEType:@"application/pdf" textEncodingName:@"UTF-8" baseURL:baseURL];
}

我应该如何改进代码,以避免内存使用问题?


1
我写了一个库,我把它放在这里,希望它对一些人有用,或者激励他们编写自己的解决方案。如果你同意的话。https://github.com/thibaultCha/TCBlobDownload - thibaultcha
3个回答

40
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse*)response {

    filename = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:save_name]; // filename is in .h file

    [[NSFileManager defaultManager] createFileAtPath:filename contents:nil attributes:nil];
        file =
[[NSFileHandle fileHandleForUpdatingAtPath:filename] retain];// file is in .h 

//if (file)     {
//
//      [file seekToEndOfFile];
//  }
 }

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSD
ata *)data {

 if (file)  { 

        [file seekToEndOfFile];

    } [file writeData:data]; 

}

- (void)connectionDidFinishLoading:(NSURLConnection*)connection { 

[file closeFile]; 

}

使用Mobihunterz发布的更简单的didReceiveData()方法是否存在缺点?看起来简单而干净。 - drfence
1
DRFENCE的didReceiveData()函数很好,但每次调用该函数时,它都会打开并关闭特定的文件'file1'。而在下载较大的文件时,该函数将被调用多次。因此,由于每次打开/关闭的开销,执行可能会有点慢。而上述函数只在此方法中写入文件,并在开始和结束时打开和关闭文件。 - Paresh Thakor
1
在connection:didReceiveData:中,seekToEndOfFile是否是不必要的?writeData:文档说“如果接收器是文件,则写入发生在文件指针的当前位置。写入数据后,该方法通过已写入的字节数推进文件指针。” - qix
@Linus 感谢您的标记,但我没有检查过删除这些内容。 - Paresh Thakor

17

如果数据非常大,为什么不在接收数据时将其写入文件,而是要将其保存在NSData对象中呢?


1
jpm:您可能需要查看NSFileHandle类。 - Daniel Dickison
Ben,你说得完全正确。我使用NSFileHandle重写了我的类,以避免将整个文件保存在内存中,现在看起来效果好多了。也感谢Daniel的提示! - jpm

6

我正在使用

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
    filename = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:save_name];
    NSFileHandle *file1 = [NSFileHandle fileHandleForUpdatingAtPath: filename];
    [file1 writeData: data];
    [file1 closeFile];
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接