请教一个关于UIPageViewController的内存java 消耗内存代码问题

今日热门版块
请教一个关于UIPageViewController的内存消耗问题
1784 次浏览
0 位用户参与讨论本周问答荣誉榜
本月问答荣誉榜
含有标签"memory"的问题
使用zxingWidget在Analyze时有蓝色提示,上架审核的时候能不能通过,如果不能通过又该如何解决?
&&&&&&& NSArray * goodsAry = [allTable objectForKey:@"GOODS"];
&&&&&&& if ([db open])
&&&&&&&&&&& [db beginTransaction];
&&&&&&&&&&& BOOL isRollBack = NO;
&&&&&&&&&&& @try
&&&&&&&&&&& {
&&&&&&&&&&&&&&& for (int i=0; i&goodsAry. i++)
&&&&&&&&&&&&&&& {
&&&&&&&&&&&&&&&&&&& NSDictionary * gds = [goodsAry objectAtIndex:i];
&&&&&&&&&nb...
我是在使用相机拍照,找了几张,期间发出过内存警告,在要点击查看拍得照片时,发生crash,打了断点,发现没用进入浏览的方法就crash,报的是 Terminated due to Memory Pressure.请问这是什么原因?
我的UIScrollView中有一个UIImageView,UIImageView的image是一张100多k的图片,UIImageView上再加一层UIView作为涂鸦层,当UIScrollView放大的时候,UIImageView会随着放大,当然UIView也一样跟着UIImageView一起放大。
OK,涂鸦前,内存使用是3-4M;
但是当我开始在涂鸦层进行涂鸦的时候,内存立马变成29M。
也就是在涂鸦层的drawRect中开始调用CGContextBeginPath(context)这个函数,也只有这个函数,内存就会猛增。
之后无论你画不画,内存都是29M多。
我想问这种情况是正常的吗,调用CGContextBeginPath(context)后需不需要再做什么处理可以使内存降下来,或者有什么方法可以让涂鸦时内存少一点?
由于项目需要,我得UITableView cell需要对应其DataSource中对象作单个重用,即Identifier根据DataSource对象的属性创建。问题是,当我remove掉DataSource中某些对象,调用UITableView reloadData方法。数据对象的dealloc很快被调用,Cell的dealloc确一直没调用。所以内存一直未降下取。求高手指点,如何释放掉内存,是否UITableView需要设置
用瀑布流加载大图时received memory
warning导致crash,用xcode看占用的内存不是很大,也就几十兆,但是还是收到了内存警告,查看过缓存也不大的,而且缓存应该不占用内存
的。但是用小图加载就没问题,我的疑惑就是明明内存不大,为什么还收到了警告,难道是下载大图导致的?我用的是SDWebImage。
我用其他程序试过了占用内存100多兆都没收到警告,均是真机调试
& & & &最近遇到一个问题,关于使用UIPageViewController做一个类似书本翻页的程序,但是发现它的内存开销非常大,每次翻页的时候它都会加载到内存中,翻过去的页面不会释放内存。
& & & &我看到这个方法它说每次调用都会返回一个新的实例变量,我想问题应该在这里,有没有其他方法可以复用?
- (PageContentViewController&*)viewControllerAtIndex:(NSUInteger)index
& &&if&(([self.pageImages&count] ==&0) || (index &= [self.pageImagescount])) {
& & & &&return&
& &&// Create a new view controller and pass suitable data.
& &&PageContentViewController&*pageContentViewController = [self.storyboardinstantiateViewControllerWithIdentifier:@"PageContentViewController"];
& &&pageContentViewController.imageFile&=&self.pageImages[index];
& &&pageC...
使用FMDataBaseQueue 多线程操作数据库的时候 Error calling sqlite3_step (21: out of memory) rs &出问题了。有人知道怎么解决嘛?
- (FMResultSet&*) selectWithSQLStr:(NSString&*) sql {
& &&__block&FMResultSet&*rs =&
& &&dbQueue&= [FMDatabaseQueue&databaseQueueWithPath:dbName];
& & [dbQueue&inDatabase:^(FMDatabase&*db) {
& & & &&FMResultSet&*set = [db&executeQuery:sql];
& & & &&if&(!set) {
& & & & & & [self&dbFail:sql];
& & & & & &&return&;
& & & & rs =
& & & & [set&close];
& &&return&
可是在instruments中的allocations中如图
live bytes还很小 不知道到底有没有问题 啥情况啊。。。。。。。
在真机上调试,发现将应用按HOME键到后台,Xcode有时会提示
Terminaterd due to memory pressure,再打开应用就变成重新打开了,
请问是怎么回事,应该怎么解决。。。
我为了测试,就在工程里面写了一个button,让他去请求照相功能
在工程里arc是打开的
就这么点的小工程他也报这个警告,
是手机的问题还是工程用了arc的问题呢
哦,对了,我的手机是4s的
请哥哥们指点一下
&这是我的代码
-(void)dosome
& & static BOOL been_here_before=NO;
& & if (been_here_before) {
& & }else{
& & & & been_here_before=YES;
& & if ([self isCameraAvailable]&&[self doesCameraSupportTakingPhoto]) {
& & & & UIImagePickerController *controller=[[UIImagePickerController alloc]init];
& & & & controller.sourceType=UIImagePickerControllerSourceTypeC
& & & & NSString *require_media_type=(__bridge NSString *)kUTTypeI
& & & & controller.mediaTypes=[[NSArray alloc]initWithObjects:require_media_type, nil];
& &nbsp...
不好意思,这个问题本来是在stackflow网上发的,但是权限不够,所以就直接copy过来了,请见谅!
& & &what does this "memory" mean in xcode5.0? does it represent the memory that my running app project consumed ? and why i found it is always become a little bigger every few seconds.
and the below is ios instruments leaks profile running interface,which is my app project's running memory , does it have any relation with memory figure in the above picture xcode5.0 interface? thanks.
&&&&&&&&最近我发现app中UIWebView在加载时很容易就收到内存警告,在iPad3中,RAM是1G,但我的程序使用到100M时,程序就会收到memory warning,继续则会crash。但是我自己做了一个Demo,不涉及UIWebView,只是不断分配内存,看Demo能最大使用多少系统内存,结果发现,当Demo使用到快600M时发出memory warning,624M时程序崩溃。但是我用UIwebView来尝试,同样的,内存使用达到130M左右时就会收到memory warning,继而出现崩溃。
&&&&&&& 所以我想是不是iOS对UIWebView的内存使用有限制,不允许UIWebView占用太多内存,有那位大神了解的,帮忙解释下,小弟再次多谢了!!!&&& ...
出现如图所示的内存泄露,希望各位大神能帮帮忙,谢谢UIPageViewController替换方案 - 简书
UIPageViewController替换方案
文接上一篇。上篇中总结了UIPageViewController的几个不可接受缺陷:1.在Scroll style下UIPageViewController的setViewControllers方法调用导致缓存设置不正确的缺陷以及针对这个缺陷改进方案引发的另一个快速连续切换问题;2.在低配设备上的性能缺陷。针对这些问题本文通过自定义的方式来解决。并记录分享在模拟替换过程中遇到的麻烦及解决方案。文中及代码中如有任何形式的错误、疑问欢迎在留言区提出。
交互切换:用户通过屏幕的手势操作切换,例如滑动。非交互切换:通过方法调用来导航切换,例如选择segment选项来触发切换方法。交互动画:用户通过屏幕手势参与的切换动画,动画无固定时长随用户交互变化。非交互动画:通过方法调用的切换,所带有的固定时长动画。
GitHub上的替代方案
Git上流行的替代方案:、。这两个都是基于scroll view做的定制。适配了很多需求使用场景。但是它们有两个关键点没有考虑:生命周期管理、 非交互动画。这两个解决方案在child controllers生命周期管理时,不能以交互切换、非交互切换的起止点来区分willAppear/didAppear,willDisappear/didDisppear等方法调用顺序与时间间隔;动画方面以禁止非交互切换动画的方式来规避非相邻index切换时动画突兀的问题(非相邻index切换,会快速scroll过中间间隔的page,且有些page可能并未添加到scrollView上)。本文中涉及的业务模块基于UIPageViewController构建,只有保证生命周期管理顺序上完全一致才能使项目的修改最小;另外,scroll view原生offset动画在非相邻index切换时会很突兀;也为了满足需求的前提下最大限度模拟UIPageViewController的功能,最终采取自定义控件的方式来解决问题。
Step1:问题分解
管理多个controller和view。
支持页面导航切换,包括交互切换,非交互切换(例segment bar)
保证与UIPageViewController导航时child controller的生命周期方法调用顺序完全相同(包括交互、非交互切换)
模拟UIPageViewController的平滑切换,在非相邻index切换时仍与相邻页面切换效果相同。
在低配设备(iPhone4、4s等)child controller初始化之后能在大部分情况下保证切换速度、避免页面卡顿。
标黑 部分问题是替换方案的关键,将在Step 3 趟坑时中细述
Step2:初步方案
UIScrollView提供了分页效果、手势处理以及交互操作中多个时机的代理回调方法。从这些代理方法入手可以获得交互切换、非交互切换的时机与起止点。管理child controller方面,UIViewController即可胜任。
child controller生命周期的切换与模拟需要花一些功夫。下文中会详细描述生命周期模拟过程。
动画问题,scroll view的setContentOffset:animated:方法效果上在不相邻index切换回跨过中间页面,很突兀不可接受。本文将通过自定义动画模拟来解决。
为了保证切换速度避免卡顿。编码时尽量避免频繁地 child view controller。只在第一次用到child view controller的时候对其进行addChildViewController操作。这样在下次切换的时候只需要关注child controllers的生命周期调用即可。
Step3:开始趟坑
1号坑:生命周期管理方式选用
一般情况下,child controller的生命周期调用是通过parent controller传递的。而parent controller生命周期方法首次传递是通过对child controller的添加/删除/切换操作来实现的,在此之后child controller的生命周期则随parent controller的生命周期一起调用。显然这种中规中矩的方式遇到了瓶颈,因为child controller的添加/删除/切换操作具有事务性。所以最终改成了一种直接控制child controller生命周期顺序的方式。下文瓶颈 中详述。
Note:这里可以复习一下的相关知识
子控制器生命周期方法的首次被调用依赖于添加/删除/切换操作。而这些操作都必须将对view的操作包夹在操作过程中(例:),即对child controller的添加/删除/切换这三种操作均具有事务性(其中包括controller操作add/remove、view操作)。曾经尝试过将view的操作分离出来单独add/remove,但这将直接导致生命周期调用顺序错误或者不调用,因为直到view显示在页面上整个添加操作才算完成。
如果通过频繁地添加(willAppear、didAppear)/删除(willDisappear、did Disappear)/切换(前两个操作综合)child controllers的方式来模拟生命周期调用。即当非交互切换页面时(点击segment通过代码showPageAtIndex:),效果毫无问题。但是,频繁的添加删除controller这样带来的卡顿问题是这种方案的缺陷1。
当用户通过滑动scroll view来切换页面时,在交互切换未完成时添加新child controller。将会造成一种现象:用户取消滑动操作或者来回多次滑向相邻的index时,这种设计将会更加频繁地调用child controller的添加删除。缺陷2
缺陷2其实可以通过scroll view的代理方法在scroll view彻底切换到新的index时再调用child controller操作,就可以避免无谓的调用而引起卡顿。但是,因为view的操作和child controller的操作是绑定的事务性的。因为直到scroll view滑动到新的index才会添加新view。这期间是看不到新view的缺陷3
考虑过,可以预加载的方式来解决相邻页面交互切换滑动的问题,但是无论预加载的是view还是controller,要么会引起卡顿问题、要么会引起生命周期错误调用问题。
解决方法:
这三个方法可以解决这个瓶颈:
在UIViewController的子类中可以重写这个方法,return YES将会把生命周期自动传递给childControllers,NO将不会自动传递生命周期。
当实现一个container controller时,使用这些方法来通知child合适调用appear、disappear方法。而不是直接调用。第一个参数YES表示要显示页面调用willAppear方法,NO表示要让页面小时调用willDisappear方法。
这个方法要与上一个方法成对出现。上一个方法的效果会调用willXXX操作,这个方法会调用didXXX操作。不成对会导致生命周期调用错误。
就是说可以重写shouldAutomaticallyForwardAppearanceMethods方法以return NO的方式规避的不合适的生命周期调用。并通过beginAppearanceTransition和endAppearanceTransition方法在合适时机管理生命周期。这样既解决了缺陷1、 缺陷2、 缺陷3又给模拟UIPageViewController的生命周期调用顺序提供了一种新方式。
func scrollViewDidScroll(scrollView: UIScrollView) {
if lastGuessIndex != guessToIndex &&
guessToIndex != self.currentPageIndex &&
self.guessToIndex &= 0 &&
self.guessToIndex & maxCount
self.gy_pageViewControllerWillShow(self.guessToIndex, toIndex: self.currentPageIndex, animated: true)
self.delegate?.gy_pageViewController?(self, willTransitonFrom: self.pageControllers[self.guessToIndex],
toViewController: self.pageControllers[self.currentPageIndex])
self.addVisibleViewContorllerWith(self.guessToIndex)
self.pageControllers[self.guessToIndex].beginAppearanceTransition(true, animated: true)
Solve problem: When scroll with interaction, scroll page from one direction to the other for more than one time, the beginAppearanceTransition() method will invoke more than once but only one time endAppearanceTransition() invoked, so that the life cycle methods not correct.
When lastGuessIndex = self.currentPageIndex is the first time which need to invoke beginAppearanceTransition().
if lastGuessIndex == self.currentPageIndex {
self.pageControllers[self.currentPageIndex].beginAppearanceTransition(false, animated: true)
if lastGuessIndex != self.currentPageIndex &&
lastGuessIndex &= 0 &&
lastGuessIndex & maxCount{
self.pageControllers[lastGuessIndex].beginAppearanceTransition(false, animated: true)
self.pageControllers[lastGuessIndex].endAppearanceTransition()
func scrollViewDidEndDecelerating(scrollView: UIScrollView) {
let newIndex = self.calcIndexWithOffset(Float(scrollView.contentOffset.x),
width: Float(scrollView.frame.size.width))
let oldIndex = self.currentPageIndex
self.currentPageIndex = newIndex
if newIndex == oldIndex {//最终确定的位置与其实位置相同时,需要重新显示其实位置的视图,以及消失最近一次猜测的位置的视图。
if self.guessToIndex &= 0 && self.guessToIndex & self.pageControllers.count {
self.pageControllers[oldIndex].beginAppearanceTransition(true, animated: true)
self.pageControllers[oldIndex].endAppearanceTransition()
self.pageControllers[self.guessToIndex].beginAppearanceTransition(false, animated: true)
self.pageControllers[self.guessToIndex].endAppearanceTransition()
self.pageControllers[newIndex].endAppearanceTransition()
self.pageControllers[oldIndex].endAppearanceTransition()
//Reset for calculation in next interaction
self.originOffset = Double(scrollView.contentOffset.x)
self.guessToIndex = self.currentPageIndex
self.gy_pageViewControllerDidShow(self.guessToIndex, toIndex: self.currentPageIndex, finished:true)
self.delegate?.gy_pageViewController?(self, didTransitonFrom: self.pageControllers[self.guessToIndex],
toViewController: self.pageControllers[self.currentPageIndex])
2号坑:UIPageViewController生命周期顺序模拟
UIPageViewController生命周期规律
UIPageViewController生命周期方法调用场景,可以根据用户的交互方式加以区别:交互切换 、 非交互切换。先看看它的生命周期管理顺序。
非交互切换:
这种导航交互比较简单,动画具有固定时间大概是0.3秒左右。而且,在非交互的导航切换中,index是否相邻顺序都是一样的。child controllers的生命周期调用顺序是:
** Will Appear 0
** Will Disappear 1
//这里大概会有0.3秒的动画时间
** Did Appear 0
** Did Disppear 1
交互切换:
有交互切换情况会复杂很多,切换动画是根据用户的操作来决定的。而且涉及到用户的取消操作即来回滑动。不过一般只能切换到相邻的index,连续切换(第一次滑动松手Decelerate动画未结束时,马上交互开始下一次滑动)的情况也会进行特殊处理
1. 滑动切换到相邻index:
//交互开始刚进入到新index立即执行
** Will Appear
** Will Disappear
//间隔时间:由交互时间+松手后Decelerate动画时间决定
** Did Appear
** Did Disppear
2. 从index1滑向index2紧接着反向滑动到index0(取消一次):
//从index1滑向index2
** Will Appear
** Will Disappear
//取消滑向index2并离开,反向滑向index0
** Will Appear
** Will Disppear
** Did Disppear
//松手完成交互,Decelerate动画完成剩余offset的偏移。
** Did Appear
** Did Disppear
3. 从index1滑向index2紧接着反向滑动到index0再反向滑向index2(取消两次):
//第一次滑向index2
** Will Appear
** Will Disappear
//第一次取消并反向滑向index0
** Will Appear
** Will Disappear
** Did Disappear
//第二次取消并反向滑向index2
** Will Appear
** Will Disappear
** Did Disppear
//松手完成交互,Decelerate动画完成剩余offset的偏移。
** Did Appear
** Did Disppear
4. 连续切换(第一次滑动松手Decelerate动画未结束时,马上交互开始下一次滑动):
** Will Appear
** Will Disppear
** Will Appear
** Will Disppear
** Did Disppear
** Will Appear
** Will Disappear
** Did Disppear
** Did Appear
事实上,每次操作的调用顺序都是不稳定的,虽然结果取决于操作的速度和滑动时机衔接,但这已经毫无规律可言。可见在这点上UIPageViewController也并没有做处理。我们暂且忽略这种情况。
生命周期一般规律:
一个Will Appear可以没有Did Appear与之对应;
一个Will Disappear一定有一个Did Disappear与之对应;
只有最终确定导航到的index才会调用Did Appear,即整个交互过程中Did Appear只调用一次;
非交互切换:
模拟scroll view的导航动画,并在动画之前调用,在动画结束回调中调用即可。代码:
// Aciton closure before simulated scroll animation
let scrollBeginAnimation = { () -& Void in
self.pageControllers[self.currentPageIndex].beginAppearanceTransition(true, animated: animated)
if self.currentPageIndex != self.lastSelectedIndex {
self.pageControllers[self.lastSelectedIndex].beginAppearanceTransition(false, animated: animated)
/* Scroll closure invoke setContentOffset with animation false. Because the scroll animation is customed.
* Simulate scroll animation among oldSelectView, lastView and currentView.
* After simulated animation the scrollAnimation closure is invoked
let scrollAnimation = { () -& Void in
self.scrollView.setContentOffset(self.calcOffsetWithIndex(
self.currentPageIndex,
width:Float(self.scrollView.frame.size.width),
maxWidth:Float(self.scrollView.contentSize.width)), animated: false)
// Action closure after simulated scroll animation
let scrollEndAnimation = { () -& Void in
self.pageControllers[self.currentPageIndex].endAppearanceTransition()
if self.currentPageIndex != self.lastSelectedIndex {
self.pageControllers[self.lastSelectedIndex].endAppearanceTransition()
self.gy_pageViewControllerDidShow(self.lastSelectedIndex, toIndex: self.currentPageIndex, finished: animated)
self.delegate?.gy_pageViewController?(self, didLeaveViewController: self.pageControllers[self.lastSelectedIndex],
toViewController: self.pageControllers[self.currentPageIndex],
finished:animated)
交互切换:
有交互切换较为复杂的地方就是要考虑到取消的情况,而这些情况在生命周期方法中并没有很好地给与区分。我们只能根据交互过程中scroll view的property以及缓存的一些参数来判断用户的行为。首先,交互切换的结束代理方法是固定的:scrollViewDidEndDecelerating。其次,我们要通过对方法scrollViewDidScroll的调用情况加以区分,区别用户的交互动作的开始和变化等行为。
func scrollViewDidScroll(scrollView: UIScrollView) {
//首先scrollView在dragging状态下确定其为交互切换。
if scrollView.dragging == true && scrollView == self.scrollView {
let offset = scrollView.contentOffset.x
let width = CGRectGetWidth(scrollView.frame)
//上一次操作猜测的用户将要滑向的lastGuessIndex
let lastGuessIndex = self.guessToIndex & 0 ? self.currentPageIndex : self.guessToIndex
//计算本次用户将要去往的index:并缓存到变量guessToIndex
if self.originOffset & Double(offset) {
self.guessToIndex = Int(ceil((offset)/width))
} else if (self.originOffset & Double(offset)) {
self.guessToIndex = Int(floor((offset)/width))
let maxCount = self.pageControllers.count
//如果上一次猜测的和本次猜测的有变化,即用户取消了上一次操作做了一次反向滑动。当然,所有猜测的index应该在安全范围内且所有猜测的页面不应该是当前显示的currentPageIndex。
if lastGuessIndex != guessToIndex &&
guessToIndex != self.currentPageIndex &&
self.guessToIndex &= 0 &&
self.guessToIndex & maxCount
self.gy_pageViewControllerWillShow(self.guessToIndex, toIndex: self.currentPageIndex, animated: true)
self.delegate?.gy_pageViewController?(self, willTransitonFrom: self.pageControllers[self.guessToIndex],
toViewController: self.pageControllers[self.currentPageIndex])
//如果guessToIndex对应child controller还未添加则添加。然后调用生IM那个周期will appear方法。
self.addVisibleViewContorllerWith(self.guessToIndex)
self.pageControllers[self.guessToIndex].beginAppearanceTransition(true, animated: true)
Solve problem: When scroll with interaction, scroll page from one direction to the other for more than one time, the beginAppearanceTransition() method will invoke more than once but only one time endAppearanceTransition() invoked, so that the life cycle methods not correct.
When lastGuessIndex = self.currentPageIndex is the first time which need to invoke beginAppearanceTransition().
//只有交互初始化的时候lastGuessIndex 才会等于 self.currentPageIndex也只有这一次才需要调用当前页面的will disappear方法。
if lastGuessIndex == self.currentPageIndex {
self.pageControllers[self.currentPageIndex].beginAppearanceTransition(false, animated: true)
//如果lastGuessIndex和当前的页面不是一个页面,就要调用其will disappear方法和did disappear方法。
if lastGuessIndex != self.currentPageIndex &&
lastGuessIndex &= 0 &&
lastGuessIndex & maxCount{
self.pageControllers[lastGuessIndex].beginAppearanceTransition(false, animated: true)
self.pageControllers[lastGuessIndex].endAppearanceTransition()
至此,经过能够完全模拟UIPageViewController的child controller 生命周期方法调用。
生命周期调用顺序.gif
3号坑: 交互动画效果
前文提到:在非相邻index导航切换的动画会很突兀。有交互的导航,是不需要考虑这个问题的,因为只能够切换到相邻index。在无交互情况下,才需要去模拟切换动画。
这里,需要考虑的复杂情况主要是:在一次导航切换动画没有完成的时候,马上又开启下一次的导航切换。前文说过UIPageViewController在做这一操作的时候,偶尔会出现最终页面错乱的问题。而本文代码在导航结果上不会出现问题,但是动画上需要处理。
考虑在第一次动画未执行结束就开启第二次动画的时候,提前结束第一次动画:
交互动画打断.gif
Step4:性能对比
下面的性能测试对比都是基于iPhone6 Plus/iOS9.3:
在静止状态下,新老控件的CPU占用率很小很小甚至不到1% 。当交互、非交互快速切换时,我们可以看到(图4.1 - 4.4)GYPageViewController的CPU占用率明显优于UIPageViewController:
图4.1 UIPageViewController快速非交互切换_CPU.png
图4.2 UIPageViewController快速交互切换_CPU.png
图4.3 GYPageViewController快速非交互切换_CPU.png
图4.4 GYPageViewController快速交互切换_CPU.png
新老控件的内存(Memory)占用方面也存在很大差异:
从图4.5中内存占用图标的波动情况可以看出UIPageViewController在快速切换的时,会尽可能快地释放掉不用的controller及其view(主要是view)以保证内存占用较小,所以图标指标先才会频繁的波动。(这并不是引起低配设备卡顿的直接原因。)这和文章开头关于UIPageViewController设计的介绍是一致的。
图4.5 UIPageViewController快速切换_Memory.png
从图4.6 中可以看到起内存占用在所有页面都缓存之后是趋于稳定的。这样是以空间换时间,来换取切换child controller时的低CPU占用。在实际测试中(包括iPhone4s/iOS7、iPhone6 Plus/iOS9.3)这样的牺牲在页面卡顿方面有了明显改善。
图4.6 GYPageViewController快速切换_Memory.png
在两个测试例子当中,主要的内存差异主要来源于view的内存占用。
Step5:总结
新的解决方案在需求上能够很好地解决UIPageViewController功能方面的缺陷(缓存设置不正确等);在性能上也优化了在低配设备上由于切换child controller及其view而引发的卡顿;而且弥补了网上现行方案生命周期管理顺序不正确、非相邻index切换动画等的问题。但新方案并非在所有方面都优于UIPageViewController,至少在内存占用上是有劣势的。(一个相当复杂的child controller及其view内存占用大概在(3~4M),简单页面大概在0.5M以内,就这点看维护十几个以内的页面还不成问题。)所有的取舍应该以自己的需求为准。
GYPageViewController解决的问题
1.从设计思路上避免了UIPageViewController切换页面时的缓存Bug2.生命周期与UIPageViewController完全一致且解决了其连续快速切换生命周期调用错乱的问题。3.用缓存控件换取切换child conroller的时间,避免瞬间CPU使用率飙升问题。4.通过缓存限额节省内存,并且处理内存警告。使内存占用量可控且做了一场处理。5.解决UIPageViewController连续快速切换的页面闪白问题(页面在使用时未及时添加或提前移除)
Step6:TO DO
在整个替代方案中,仍有一些地方值得继续优化:
维护缓存池,移除长期不使用的child controller及其页面以节省内存。
Step7:更新中已解决
非交互动画,在前一次动画未结束时开始新一次动画,不强制结束上次移动而是在当前时刻暂停并以这个状态为起点,开始新动画。
在快速连续交互切换时,生命周期规则重新整理并实现有规律调用。
Step7:更新中已解决
Step7:更新
缓存策略:
内存占用方面,主要是container controller的childViewControllers持有过多不必要的child controller所导致。之前的想法是用空间换取切换(add/remove)child controller的时间。但综合考虑内存等因素对早前方案做一个折衷:缓存一定数量的常用child controllers并且在适当时机清理多余的controllers。
缓存简图.jpg
Memory Cache的职责:1.避免多次调用dataSource方法,即避免频繁通过delegate创建常用的child controller带来的性能损耗。2.提供缓存child controllers限额3.提供淘汰策略与时机childViewControllers职责与限制:1.存储具体child controllers指针。空间换时间的功臣。2.需要淘汰时机,稳定状态下数量与Memory Cache一致。
因为存的是child controllers的指针,两份存储并不会导致内存double。难点在于两者的同步问题:
1.当element从Memory Cache淘汰的同时检查是否为当前操作页面的相关交互页面。(非交互切换:原始页面、目的页面为相关交互页面;交互切换:当前猜的的目的页面及其左右两个页面为相关交互页面)如果检查非相关交互页面则直接清理,否则加入到延迟清理集合并在最终的稳定态开始时清理(切换过程彻底结束时)。2.过滤延迟切换队列:当频繁切换时,延迟队列里的element可能是最终切换停止时的page。如果不过滤,将根据交互情况出现最终页面被错误移除的情况。需要在延迟清理集合的时候,过滤掉最终的目标页面。3.在连续交互切换时,很长时间不能达到一个稳定态。即很多根据稳定态计算的临时数据是有偏差的。需要在连续切换过程中随时纠偏以保证缓存的正确清理。
快速连续切换生命周期规律调用:
这个问题和缓存同步问题的第三点的问题原因是一样的。在连续切换交互中,数据计算有偏差会导致猜测的目标页面不准确,生命周期调用错乱。在达到稳定态之前的任何交互节点(例如手指离开屏幕)都需要对数据计算纠偏。纠偏主要是不断调试和优化计算的工作,这里有兴趣可以参看下Git上的代码。
网易贵金属移动组}

我要回帖

更多关于 请教一个问题 英文 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信