19

由于 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];
}
4

5 回答 5

5

我认为你的假设是正确的,因为我也经历过类似的行为:当你翻到下一页时,也是为了很好地制作动画,新页面在旧页面被释放之前分配,并且需要一些时间旧的将被释放。因此,当您翻转得足够快时,分配对象的速度比释放对象的速度要快,最终(实际上,很快),您的应用程序由于内存使用而被终止。如果您遵循Instruments.

IMO,您有三种方法:

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

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

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

在内存使用优化方面没有灵丹妙药,这是一项非常详细和艰苦的工作,但是如果您查看代码并应用这 3 条准则,则 IME 将能够减少应用程序的内存配置文件。特别是,检查 Instruments 中的内存分配峰值并尝试了解它们的相关性非常强大。

于 2012-05-31T13:56:04.817 回答
2

这是我所做的额外更改,可能有人会觉得有帮助:

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

我使用苹果的默认 PageViewController 项目作为模板,所以我将使用该项目中定义的术语。

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

在我的作为 UIPageViewController 委托的 EbookViewController 中,我捕获了手势识别器,并将 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;
}

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

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

于 2012-09-28T17:37:22.313 回答
0

目前 iOS5 中有一个 bug 会导致滚动视图泄漏少量内存。

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

您可以在模拟器中模拟内存不足警告(硬件 -> 模拟内存不足警告)。或者你可以通过代码来完成,(只记得在调试后删除,因为这会让你的应用程序被拒绝!)

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

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

如果您正在创建大量临时对象(不是属性或未分配的对象),则插入一个自动释放池:

@autoreleasepool {

}

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

于 2012-05-28T01:41:53.770 回答
0

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

  1. 要强制卸载视图,您可以覆盖viewDidDisappear:出现在UIPageViewController.

    代码如下所示:

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

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

  2. 方法的工作方式也可能存在一些混淆UIPageViewControllerDataSource——你可能有一些“混合线”。在此处检查接受的答案。

于 2012-06-02T16:45:10.807 回答
0

它可能是由渲染引起的。当flipper过快时,重绘“Page”占用的内存和CPU会迅速增加。如果你在 UIPageViewController 中使用的视图是基于 CALayer 的,并且页面太多,那么翻转太快肯定会导致 App 崩溃。

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

于 2012-07-19T08:38:39.433 回答