zoukankan      html  css  js  c++  java
  • iOS UIPageViewController缺陷

    为什么弃用UIPageViewController?
    问题1:
    设置UIPageViewController为UIPageViewControllerTransitionStyleScroll且调用setViewControllers:direction:animated:completion:传递参数animated YES时,会引发一系列症状,例如:
        1.    缓存页面导航设置不正确,Page View Controller会导航到错误的页面。
        2.    删除上一页view controller(已经切换完成后)失败,仍然可以scroll回到一个空白页。
    这是UIPageViewController的bug,当且仅当UIPageViewController为.Scroll时,调用setViewControllers:direction:animated:completion: 且方法参数animated YES才可能出现。
错误的原因是,当使用.Scroll风格时,UIPageViewController做了一些内部的缓存排序,当调用setViewControllers:direction:animated:completion:时,它没有清空其内部缓存。它认为它已经知道前一个页面的存在,当它调用前一个页面的时候,就不会去调用dataSource方法或者调用错误。
根据这个描述,其实可以对之前的错误代码做相应的处理,通过再次调用setViewControllers方法强制重新调用dataSource方法以更新缓存。例如:
    @weakify(self)
     [self.pageViewController setViewControllers:@[targetViewController]
                                       direction:direction
                                        animated:animated
                                      completion:^(BOOL finished)
     {
         @strongify(self)
         if (finished) {
             [self.pageViewController setViewControllers:@[self.pages[index]] direction:direction animated:NO completion:NULL];               
             // bug fix for uipageview controller
         }
     }];
    很不幸这种做法引发了崩溃(iOS7/8/9):
    Assertion failure in
[_UIQueuingScrollView_replaceViews:updatingContents:adjustContentInsets:animated:], /SourceCache/UIKit_Sim/UIKit-3318.16.14/_UIQueuingScrollView.m:383**
    分析后发现我们忽略的一个重点,setViewControllers这个方法是更新页面的操作应该在主线程中调用。修改代码如下:
     @weakify(self)
     [self.pageViewController setViewControllers:@[targetViewController]
                                       direction:direction
                                        animated:animated
                                      completion:^(BOOL finished)
     {
         @strongify(self)
         if (finished) {
             dispatch_async(dispatch_get_main_queue(), ^
                            {
                                if (self) {
                                    [self.pageViewController setViewControllers:@[self.pages[index]] direction:direction animated:NO completion:NULL];               
                                    // bug fix for uipageview controller
                                }
                            });
         }
     }];
    代码看起来虽然不怎么优雅,但好在这样可以继续使用UIPageViewController。直到我们发现了Fabric上的崩溃和上段解决方案代码引发的新问题:
    Fabric崩溃:

    UIPageViewController_Fabric.jpg
    这个崩溃在iOS7/8/9上都有出现。显然这和Apple Developer Forums中提到的问题现象是一样的:No view controller managing visible view:
    但Apple Developer Forums中的问题,是因为没有在主线程中执行第二次setViewControllers方法导致的。我们的代码里已经做了相应处理,但仍然有小规模数量的崩溃。很不幸,暂时还没找到Fabric崩溃的具体复现方式。
    上图说明,上段代码并没有完全解决page view controller页面缓存不正确的bug。只是不会那么频繁地出现。
    引发新问题:
    此外还发现:当通过上段代码导航切换index的时候,如果第一次导航(setViewControllers)动画没有结束时,马上开始第二次导航(setViewControllers)。很容易复现问题:页面没有停在最终的那个index上。这是因为第二次主动调用的带动画的setViewControllers执行在第一次调用的回调的无动画的setViewControllers之前导致的。
    问题2:
    切换childViewController引起的卡顿问题很严重。在iPhone4、4s、5、5c、甚至5s上都会有不同程度的卡顿问题:
一般情况下,page view controller切换页面的资源消耗至少相当于调用一次transitionFromViewController:toViewController:duration:options:animations:completion:。在硬件配置较低的iPhone4、4s等手机上就会有明显卡顿,如果child view controller的生命周期方法中再做一些消耗资源的操作,App甚至会因为切换导致资源占用过多、内存警告,最终引起Crash。这种情况给低配手机性能优化带来了很大障碍。
    其性能问题主要体现在,切换childController时候的CPU占用升高、以及切换时的内存频繁波动。
静止状态下,其CPU占用率位置在很低的水品甚至不到1%,当child congroller切换时,其CPU占用率如图所示(iPhone6 Plus/iOS9.3):

    UIPageViewController快速非交互切换_CPU.png

    UIPageViewController快速交互切换_CPU.png
    文章开头介绍UIPageViewController时提到过,UIPageViewController的设计更多的考虑了少占用内存,从下图中内存的波动曲线也可以看到。当频繁切换child controller时,UIPageViewController尽可能快的清理内存。快速切换时,其内存波动如下图所示(iPhone6 Plus/iOS9.3):
    总结:
    经过多个设备、系统版本的测试和网上资料的整理。问题1的处理方案是现在最主流的一种解决方案,但这样的方案仍然引发了Fabric崩溃和新的缺陷问题,从保证App质量的角度出发,这个解决方案是不可取的。问题2的性能问题虽然在高配手机上并不明显,但考虑的所有用户的体验这个缺陷也很值得去优化。
为了彻底解决这些缺陷问题,重新开发一个Page view controller控件来彻底解决UIPageViewController带来的缺陷问题。

  • 相关阅读:
    JAVA实现图的邻接表以及DFS
    对于JAVA多线程卖票小程序的理解
    我的第一篇博客
    The 'with' and 'as' Keywords
    Buffering Data
    rstrip
    MapFields和并行计算(OpenFOAM)
    Python 调用自己编写的Class
    vs2013和vs2010的配置
    Eclipse的配置
  • 原文地址:https://www.cnblogs.com/yujidewu/p/6117536.html
Copyright © 2011-2022 走看看