hpi*_*que 4 video didreceivememorywarning ios avplayer
我正在和他一起玩很多视频AVPlayer
.为了减少加载时间,我将相应的视图存储在一个NSCache
.
这可以正常工作,直到达到一定数量的视频,视频只是停止播放,甚至出现.
没有错误,日志或内存警告.特别是,我正在听UIApplicationDidReceiveMemoryWarningNotification
清除缓存但是从未收到过.
如果我删除缓存,所有视频都会以较差的性能为代价.
这让我怀疑AVPlayer
是使用来自不同进程的内存(哪一个?).当记忆达到一定限度时,新玩家将停止工作.
它是否正确?
如果是这样,是否有办法在达到此魔术限制时通知采取适当的措施(例如,清除缓存)以确保播放其他媒体?
Dav*_*d H 14
好消息和坏消息 - 好的是你可以解决问题,不好是需要工作而且有点复杂.
根本问题
你没有得到早期通知的原因是因为iOS没有发现你的应用程序已超出其内存预算,直到它已经太晚了,然后它会立即杀死它.问题与iOS(和OS X)管理文件系统缓存的方式有关.通常情况下,当文件打开时,当您读取数据时,文件数据会被转移到缓冲区中Unified Buffer Cache
(您可以谷歌获取更多信息) - 从现在开始我称之为UBC.
所以假设你有10个打开的文件,并且你已经读完了每个文件,但还没有关闭文件.好吧,所有这些数据都在UBC中.现在,如果关闭文件,缓冲区将全部释放.从技术上讲,操作系统也可以清除这些缓冲区 - 只有当它意识到内存紧张时,它才会选择先将应用程序吹走(并且可能有正当理由这样做).因此,假设您的应用程序正在显示视频,并且视频加载的方式是通过文件系统,空闲缓冲区的数量开始下降.在某些时候iOS会注意到这一点,追踪最属于你的应用程序(你的应用程序),并尽快杀死你的应用程序.
我自己在一个支持的开源项目中遇到了这个问题,PhotoScrollerNetwork.用户开始抱怨他们的项目被系统终止,就像你一样,没有任何通知.我徒劳地试图监控UBC(OS X上有API这样做,但不是在iOS上).最后,我找到了一个使用启发式的解决方案 - 监控所有内存使用情况,包括UBC,并且不超过总可用iOS内存池的50%.
所以(你可能会问) - Apple批准的解决这个问题的方法是什么?嗯,没有.我怎么知道的?因为我在2012年WWDC上与其中一个实验室的核心iOS总监进行了半小时的讨论(在其他人不知道我在谈论什 最后,在我解释了上述启发式之后,他直接告诉我,解决方案可能与他能想到的一样好.如果没有API直接监控UBC,您只能估算其使用情况并进行相应调整.
但是你说,我正在使用NSCache - 为什么系统不AVPlayer
占用那里的内存?毫无疑问,UBC的一个AVPlayer
实例可能只消耗几千K的内存 - 它是视频的开放文件,而不是iOS所占用的.
可能的解决方案
1)如果您可以将视频直接加载到NSData对象中,并将其保留在NSCache中,您很可能完全避免上面提到的UBC问题.[我不清楚AV系统是否可以做到这一点.]在这种情况下,系统应该能够在需要时清除内存.
2)继续使用原始代码,但为其添加内存管理.也就是说,当您创建AVPlayer实例时,您需要以字节为单位计算视频大小,并保持所有内存的运行记录.当您接近总设备可用内存的50%时,然后开始清除旧的AVPlayers.
码
为了完整起见,我在下面提供了PhotoScrollerNetwork的相关代码.如果你想要更多的细节,你可以仔细阅读项目 - 但是它非常复杂,所以希望花一些时间(它可以动态地进行JPEG解码,以获得大量图像,并在解码过程中将图块写入文件系统).
// Data Structure
typedef struct {
size_t freeMemory;
size_t usedMemory;
size_t totlMemory;
size_t resident_size;
size_t virtual_size;
} freeMemory;
Run Code Online (Sandbox Code Playgroud)
在您的应用程序的早期:
// ubc_threshold_ratio defaults to 0.5f
// Take a big chunk of either free memory or all memory
freeMemory fm = [self freeMemory:@"Initialize"];
float freeThresh = (float)fm.freeMemory*ubc_threshold_ratio;
float totalThresh = (float)fm.totlMemory*ubc_threshold_ratio;
size_t ubc_threshold = lrintf(MAX(freeThresh, totalThresh));
size_t ubc_usage = 0;
// Method on some class to monitor the memory pool
- (freeMemory)freeMemory:(NSString *)msg
{
// http://stackoverflow.com/questions/5012886
mach_port_t host_port;
mach_msg_type_number_t host_size;
vm_size_t pagesize;
freeMemory fm = { 0, 0, 0, 0, 0 };
host_port = mach_host_self();
host_size = sizeof(vm_statistics_data_t) / sizeof(integer_t);
host_page_size(host_port, &pagesize);
vm_statistics_data_t vm_stat;
if (host_statistics(host_port, HOST_VM_INFO, (host_info_t)&vm_stat, &host_size) != KERN_SUCCESS) {
LOG(@"Failed to fetch vm statistics");
} else {
/* Stats in bytes */
natural_t mem_used = (vm_stat.active_count +
vm_stat.inactive_count +
vm_stat.wire_count) * pagesize;
natural_t mem_free = vm_stat.free_count * pagesize;
natural_t mem_total = mem_used + mem_free;
fm.freeMemory = (size_t)mem_free;
fm.usedMemory = (size_t)mem_used;
fm.totlMemory = (size_t)mem_total;
struct task_basic_info info;
if(dump_memory_usage(&info)) {
fm.resident_size = (size_t)info.resident_size;
fm.virtual_size = (size_t)info.virtual_size;
}
#if MEMORY_DEBUGGING == 1
LOG(@"%@: "
"total: %u "
"used: %u "
"FREE: %u "
" [resident=%u virtual=%u]",
msg,
(unsigned int)mem_total,
(unsigned int)mem_used,
(unsigned int)mem_free,
(unsigned int)fm.resident_size,
(unsigned int)fm.virtual_size
);
#endif
}
return fm;
}
Run Code Online (Sandbox Code Playgroud)
当您打开视频时,请将大小添加到ubc_usage
,并在关闭时减小它.当您想要打开一个新视频时,请测试ubc_usage ubc_threadhold
,并且它超过您必须先关闭某些内容的值.
PS:您可以尝试在其他时间调用freeMemory方法,并查看,但在我的情况下,当文件打开时它几乎没有变化 - 系统似乎认为整个UBC是"免费的",因为它可以清除它,如果它需要(我猜).
归档时间: |
|
查看次数: |
2996 次 |
最近记录: |