iOS 应用程序在下载非常大的文件时耗尽内存

标签 ios objective-c memory memory-management memory-leaks

我知道我的问题的变体已经在这里以一种或另一种形式多次被问到 - 我已经广泛搜索这些帖子好几天了,我认为我的问题是独特的。请允许我描述一下:

我在 iPad 4 上有一个 iOS 应用程序,可以将大文件(260MB 到 650MB)下载到磁盘。下载期间内存使用量与下载的数据量成正比。在 500MB 左右时,应用程序会收到内存不足警告,在 650MB 左右时,应用程序会因内存不足而被终止。

我曾多次使用 Instruments 来尝试追踪分配的来源 - 我已多次运行分配、虚拟机分配、泄漏、内存监视器和事件监视器。随着内存的增加,我也拍摄了很多heapshots。 Instruments 从未显示过来 self 的应用程序的大量分配!内存保持平淡。 Leaks 也没有报告任何内容。

相反,我一直依赖一组函数来询问内核内存使用情况,这是我在另一个线程上找到的:

   vm_size_t usedMemory(void) {
    struct task_basic_info info;
    mach_msg_type_number_t size = sizeof(info);
    kern_return_t kerr = task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&info, &size);
    return (kerr == KERN_SUCCESS) ? info.resident_size : 0; // size in bytes
}

vm_size_t freeMemory(void) {
    mach_port_t host_port = mach_host_self();
    mach_msg_type_number_t host_size = sizeof(vm_statistics_data_t) / sizeof(integer_t);
    vm_size_t pagesize;
    vm_statistics_data_t vm_stat;

    host_page_size(host_port, &pagesize);
    (void) host_statistics(host_port, HOST_VM_INFO, (host_info_t)&vm_stat, &host_size);
    return vm_stat.free_count * pagesize;
}

void logMemUsage(void) {
    // compute memory usage and log if different by >= 100k
    static long prevMemUsage = 0;
    long curMemUsage = usedMemory();
    long memUsageDiff = curMemUsage - prevMemUsage;

    // if (memUsageDiff > 100000 || memUsageDiff < -100000) {
    prevMemUsage = curMemUsage;
    //NSLog(@"Memory used %7.1f (%+5.0f), free %7.1f kb", curMemUsage/1000.0f, memUsageDiff///1000.0f, freeMemory()/1000.0f);
    printf("Memory used %7.1f (%+5.0f), free %7.1f kb\n", curMemUsage/1000.0f, memUsageDiff/1000.0f, freeMemory()/1000.0f);

    //}
}

使用这个让我终于看到有东西正在下载循环中消耗内存。我使用 RequestQueue 以及以下方法进行下载:

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
    if (_filePath && _filePath.length)
    {
        if (_fileHandle == nil)
        {
            [[NSFileManager defaultManager] createFileAtPath:_filePath contents:nil attributes:nil];
            self.fileHandle = [NSFileHandle fileHandleForUpdatingAtPath:_filePath];
            _bytesWritten = _rangeStart;
        }

        [_fileHandle writeData:data];
        _bytesWritten += [data length];
        NSInteger totalBytes = MAX(0, _responseReceived.expectedContentLength) + _rangeStart;

        if (_downloadProgressHandler)
        {
            _downloadProgressHandler(_userData, _userData2, (float)_bytesWritten / (float)totalBytes, _bytesWritten, totalBytes);
        }
    }
}

即使我注释掉 [_fileHandle writeData:data] 调用,我仍然观察到内存增加!我已经仔细检查过我是否正在关闭文件句柄并将其设置为 nil,但完成后内存会发生增长。

注意:该项目使用 ARC。

目前我没有什么可尝试的了。根本不将数据写入文件仍然会导致内存增长。禁用显示下载进度的 UIStatusBar 没有帮助。我在仪器中看不到任何内容。我本来打算尝试使用 NSOutputStream 来写入文件,但我怀疑这是否会有帮助,因为根本不写入数据仍然会导致内存增长。

我尝试使用 NSURLCache 并在收到内存不足警告时清除缓存。我尝试将 nil 分配给 didReceiveData 方法中的数据,但这并没有改变任何内容。我还尝试将尽可能多的强指针更改为弱指针,但这只会导致在已释放的实例上调用选择器。

此时非常欢迎任何建议。

最佳答案

事实上,您在 Instruments 中没有看到它,这表明您的发布配置和调试配置之间存在显着差异。您在仪器下收到内存警告吗?

编辑您的方案,选择“配置文件”选项,然后选择构建配置“调试”。看看这是否与您使用“运行”时所看到的相匹配。如果是这样,那么您可以从那里使用仪器来追踪这一点。

可能性不大,但需要验证的一件事是您没有意外打开 NSZombies。如果你这样做了,你就会看到这种行为。转到您的方案并检查“运行”选项。查看“诊断” Pane 并确保未选择任何内容。

关于iOS 应用程序在下载非常大的文件时耗尽内存,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16113426/

相关文章:

android - 将 Unity3D 游戏集成到 Android/iOS Activity 中

ios - 如何在 Objective-C 中确定相同的 NSString?

objective-c - Objective-C : How to check for unread messages (number to be displayed as badge)

c - 程序处理

c - 系统如何知道数据段的结束

iOS 打印机模拟器错误 - Air Print

ios - 在 Xcode 9.0 中使用未声明的标识符

c - 内存分配和内存泄漏: C

ios - 实现模糊日期

iPhone iOS 如何显示应用程序中存储的照片的幻灯片?