Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/video/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ios 我的AVPlayer';这是我的记忆,我怎样才能找回它?_Ios_Video_Avplayer_Didreceivememorywarning - Fatal编程技术网

Ios 我的AVPlayer';这是我的记忆,我怎样才能找回它?

Ios 我的AVPlayer';这是我的记忆,我怎样才能找回它?,ios,video,avplayer,didreceivememorywarning,Ios,Video,Avplayer,Didreceivememorywarning,我正在使用AVPlayer同时播放大量视频。为了减少加载时间,我将相应的视图存储在NSCache中 在达到一定数量的视频之前,这一切都是正常的,视频从中停止播放,甚至停止出现 没有错误、日志或内存警告。特别是,我正在收听UIApplicationIDReceiveMemoryWarningNotification以清除缓存,但从未收到此消息 如果删除缓存,所有视频播放都会以性能下降为代价 这让我怀疑AVPlayer正在使用来自不同进程的内存(哪一个?)。当记忆达到一定限度时,新玩家就会停止工作

我正在使用
AVPlayer
同时播放大量视频。为了减少加载时间,我将相应的视图存储在
NSCache

在达到一定数量的视频之前,这一切都是正常的,视频从中停止播放,甚至停止出现

没有错误、日志或内存警告。特别是,我正在收听
UIApplicationIDReceiveMemoryWarningNotification
以清除缓存,但从未收到此消息

如果删除缓存,所有视频播放都会以性能下降为代价

这让我怀疑
AVPlayer
正在使用来自不同进程的内存(哪一个?)。当记忆达到一定限度时,新玩家就会停止工作

这是正确的吗


如果是这样的话,是否有办法在达到此神奇极限时得到通知,以采取适当措施(例如清除缓存)确保播放其他媒体?

好消息和坏消息-好消息是您可能会解决问题,坏消息是它需要工作,而且有些复杂

根本问题

你没有提前得到通知的原因是,iOS没有发现你的应用程序已经超出了内存预算,直到它几乎太迟了,然后它会立即终止它。问题与iOS(和OSX)管理文件系统缓存的方式有关。通常,当打开文件时,当您读取数据时,文件数据会被传输到
统一缓冲区缓存中的缓冲区中(这是一个术语,您可以在谷歌上搜索更多信息)-从现在起,我将其称为UBC

因此,假设您有10个打开的文件,并且您已将每个文件读取到最后,但尚未关闭这些文件。所有的数据都在UBC里。现在,如果关闭文件,缓冲区将全部释放。从技术上讲,操作系统也可以清除这些缓冲区——只有当它意识到内存紧张时,它才会选择先将应用程序吹走(这可能是它这样做的正当理由)。因此,假设您的应用程序正在显示视频,并且视频的加载方式是通过文件系统,可用缓冲区的数量开始下降。在某个时刻,iOS会注意到这一点,追踪谁是你的应用的最归属者,然后砰的一声,尽快杀死你的应用

我自己在一个我支持的开源项目中遇到了这个问题。用户开始抱怨他们的项目被系统终止,就像你一样,没有任何通知。我试图监视UBC是徒劳的(OSX上有这样做的API,但iOS上没有)。最后,我找到了一个使用启发式的解决方案——监控所有内存使用情况,包括UBC,并且不要超过总可用iOS内存池的50%

那么(你可能会问)-苹果批准的解决这个问题的方法是什么?没有。我怎么知道?因为我在WWDC 2012上与Core iOS的主管在一个实验室进行了半小时的讨论(在被其他不知道我在说什么的人打乒乓球之后)。最后,在我解释了上述启发之后,他直接告诉我,这个解决方案可能是他所能想到的最好的。如果没有直接监视UBC的API,您只能近似地使用它并进行相应的调整

但是你会说,我使用的是NSCache——为什么系统不考虑那里的
AVPlayer
内存?毫无疑问,这是因为UBC(一个
AVPlayer
实例本身可能只消耗几千K内存)是一个开放的视频文件,而iOS没有考虑到它

可能的解决办法

1) 如果您可以将视频直接加载到NSData对象中,并将其保存在NSCache中,则很可能完全避免上述UBC问题。[我对AV系统了解不够,不知道您是否可以这样做。]在这种情况下,系统应该能够在需要时清除内存

2) 继续使用原始代码,但添加内存管理。也就是说,当您创建一个AVPlayer实例时,您需要以字节为单位说明视频的大小,并保持所有内存的运行记录。当您接近总设备可用内存的50%时,开始清除旧的AVP播放器

代码

为了完整起见,我从下面提供了相关代码。如果你想要更多的细节,你可以仔细阅读这个项目——但是它非常复杂,所以你需要花费一些时间(它会对海量图像进行动态JPEG解码,并在解码过程中将图像块写入文件系统)

在应用程序的早期:

// 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;
}
打开视频时,将大小添加到
ubc\u usage
,关闭视频时,将其减小。当你想打开一个新的视频时,根据
ubc\u threadhold
测试ubc\u的使用情况,它超过了你必须先关闭的值


PS:你可以试着在其他时候调用FielMeMy方法,但是,在我的例子中,当文件被打开时,它几乎没有变化。系统似乎认为整个UBC是“免费的”,因为它可以清除它(如果需要的话)。您必须准备好缓存,以便在感觉项目占用了太多内存时将其丢弃。从:

NSCache类包含各种自动删除策略,这些策略 确保它不会占用过多的系统内存。这个 如果用户需要内存,系统将自动执行这些策略 其他应用程序。调用时,这些策略将删除某些项 从缓存中删除,最大限度地减少其内存占用

检查是否从缓存中获取
nil
s,如果是,则必须重新构建对象

编辑: 还值得一提的是,建议不要将大型对象存储在
NSCache

NSCache的逐出方法是不确定的,并且不是 记录在案。加入超级联赛不是个好主意-
// 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;
}