UIPageViewController在内存不足时翻转过快时崩溃

时间:2012-05-14 01:41:35

标签: ios automatic-ref-counting uipageviewcontroller

由于Xcode的UIPageViewController模板缓存了所有页面数据,我遇到了一些内存问题,因此我将其更改为动态加载页面,所以现在当我的应用程序收到内存不足警告时,它释放内存以便页面不显示但是如果用户通过点击屏幕边缘快速翻阅页面,它仍然会崩溃。我猜这是因为当调用didReceiveMemoryWarning时,它无法足够快地释放内存。如果用户缓慢翻转,它可以正常工作。我限制了用户翻页的速度,但它仍然会发生。我希望每次翻页都能释放内存,而不必等待低内存警告。我正在使用ARC。有没有办法做到这一点?或者我还能做些什么来防止这种情况发生?感谢。

编辑:

(UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{
    NSUInteger index = [self indexOfViewController:(SinglePageViewControllerSuperclass *)viewController];
    if ((index == 0) || (index == NSNotFound)) {
        return nil;
    }

    index--;
    return [self viewControllerAtIndex:index];
} 

(UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
    NSUInteger index = [self indexOfViewController:(SinglePageViewControllerSuperclass *)viewController];
    if (index == NSNotFound || index == MAX_PAGE_INDEX) {
        return nil;
    }

    return [self viewControllerAtIndex:++index];
}

5 个答案:

答案 0 :(得分:5)

我认为你的假设是正确的,因为我也经历过类似的行为:当你翻到下一页时,为了好好动画,新页面在旧的页面被解除分配之前被分配,它需要一些旧的一个被解除分配的时间。因此,当你足够快地翻转时,对象的分配速度比它们被解除分配的速度快,并且最终(实际上很快),你的应用程序因内存使用而被杀死。如果您按照Instruments中的内存分配/取消分配,翻页时的释放延迟会非常明显。

你有三种方法,IMO:

  1. 实现“轻”viewDidLoad方法(实际上,整个初始化/初始显示序列):在某些应用程序中,有意义的是,例如,加载低分辨率图像而不是高分辨率将显示的图像;或者,稍微延迟页面所需的额外资源分配(数据库访问,声音等);

  2. 使用一个页面池,比如一个三页(或5个,取决于你的应用程序)的数组,你继续“重用”,以便你的应用程序的内存配置文件保持稳定并避免尖峰; < / p>

  3. 仔细检查分配和释放内存的方式;从这个意义上说,你经常读到自动释放为释放/释放机制添加了一些“惯性”,这很容易理解:如果你有一个自动释放的对象,只有当你循环通过它时,它才会被它的释放池释放。主循环(对于主发布池,这是正确的);因此,如果您在翻页时调用了很长的方法序列,这将使发布/ dealloc稍后发生。

  4. 在内存使用优化方面没有神奇的内容,这是一个非常详细和艰苦的工作,但是如果您查看代码并应用这3条准则,IME将能够减少应用程序的内存配置文件。特别是,检查仪器中存储器分配的峰值并试图理解它们的相关性是非常强大的。

答案 1 :(得分:2)

以下是我所做的其他更改,有人可能会对此有所帮助:

基本上,如果前一个页面已经完成,我只允许新页面开始。

我使用Apple的默认PageViewController项目作为模板,因此我将使用该项目中定义的术语。

每当通过viewControllerAtIndex:请求页面VC时,我在ModelController上设置一个名为“shouldDenyVC”的布尔值到YES

在我的EbookViewController中,它是UIPageViewController的委托,我捕获手势识别器,并指定EbookViewController作为其委托:

self.view.gestureRecognizers = self.pageViewController.gestureRecognizers;
for (UIGestureRecognizer *gr in self.view.gestureRecognizers) {
    gr.delegate = self;
}

然后,我可以通过拒绝手势识别器来拒绝翻页:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:    (UITouch *)touch
{
    if (_modelController.shouldDenyPageTurn == YES) {
        return FALSE;
    }
    return TRUE;
}

最后,我在UIPageViewController委托方法_modelController.shouldDenyPageTurn = NO的末尾设置pageViewController:didFinishAnimating:previousViewControllers:transitionCompleted:

我还必须在任何预加载结束时设置_modelController.shouldDenyPageTurn = NO,以便允许翻页。

答案 2 :(得分:0)

iOS5中存在一个错误,导致滚动视图泄漏少量内存。

您是否尝试在检查分配和内存泄漏的仪器中分析应用程序?

您可以在模拟器中模拟低内存警告(硬件 - >模拟低内存警告)。或者你可以通过代码来实现(只需记住在调试后删除,因为这会让你的应用被拒绝!)

[[UIApplication sharedApplication] performSelector:@selector(_performMemoryWarning)];

如果您使用的是strongretain属性,请在完成后将其设置为nil,ARC将释放他们指向幕后的内存。

如果要创建大量临时对象(非属性或未分配的对象),则插入自动释放池:

@autoreleasepool {

}

最后,展示一些代码,我们可以更好地帮助您。

答案 3 :(得分:0)

由于您没有发布任何代码,因此很难猜出问题究竟在哪里。

  1. 要强制卸载视图,您可以覆盖viewDidDisappear:中出现的那些viewcontroller类的UIPageViewController方法。

    代码如下:

    - (void)viewDidDisappear:(BOOL)animated {
        [self didReceiveMemoryWarning];
    }
    

    如果您还有didReceiveMemoryWarning覆盖,请不要忘记从中调用[super didReceiveMemoryWarning];

  2. 对于UIPageViewControllerDataSource方法如何工作也可能存在一些混淆 - 你可能会遇到一些'混合线'。检查已接受的答案here

答案 4 :(得分:0)

可能是由渲染造成的。当鳍状肢过快时,重绘“页面”所使用的内存和CPU将迅速增加。如果您在UIPageViewController中使用的视图基于CALayer并且页面太多,则翻转太快肯定会使App崩溃。

一种解决方案是自定义图层并缓存渲染结果。仅在必须时重新呈现内容。但缓存可能会增加内存使用量。