释放 UIPageViewController 中未使用的页面
Posted
技术标签:
【中文标题】释放 UIPageViewController 中未使用的页面【英文标题】:Releasing unused pages in UIPageViewController 【发布时间】:2013-01-23 05:28:16 【问题描述】:我为基于UIPageViewController
的图画书的每个基于UIViewController
的页面使用单独的.h
、.m
和.xib
文件。每个页面都加载了动画、音乐等,大约需要 4MB 的内存。在 Instruments 中,随着每一页的加载,可用内存会下降约 4MB。当页面被翻页时,这个内存永远不会被释放。它最终会给出内存警告。 UIPageViewController
似乎将它实例化的每个页面保留在内存中并且不会卸载它。因此,当页面快速翻动时,应用程序就会崩溃。
我希望能够卸载除UIPageViewController
所需的 3 个页面之外的所有页面——前一页、当前页和下一页。我如何卸载不需要的页面,因为它们是由UIPageViewController
实例化的。
下面是UIPageViewController
从中提取的页面数组。所有页面(Page1、Page2 等)基本上只是加载图像文件、提供基本动画和音乐。
//ARRAY OF PAGES
pageArray = [[NSArray alloc] initWithObjects:
(id)[[Page1 alloc] initWithNibName:nil bundle:nil],
[[Page2 alloc] initWithNibName:nil bundle:nil],
[[Page3 alloc] initWithNibName:nil bundle:nil],
[[Page4 alloc] initWithNibName:nil bundle:nil],
[[Page5 alloc] initWithNibName:nil bundle:nil],
[[Page6 alloc] initWithNibName:nil bundle:nil],
[[Page7 alloc] initWithNibName:nil bundle:nil],
[[Page8 alloc] initWithNibName:nil bundle:nil],
// continues all the way up to page 47
[[Page47 alloc] initWithNibName:nil bundle:nil],
nil];
我省略了UIPageViewController
的标准初始化。它使用“nextPageNumber
”从上面的pageArray
中拉出正确的页面来创建一个新的页面对象。
-(void)turnPageForward
[pageController setViewControllers:[NSArray arrayWithObject:[pageArray objectAtIndex:nextPageNumber]]
direction:UIPageViewControllerNavigationDirectionForward
animated:YES completion:^(BOOL finished)
];
我尝试创建一个对象“pageIndex
”(见下文),在将其提供给pageController
后将其设置为nil。它没有用。页面前进后,页面仍然很占内存。
//PROGRAM PAGE FORWARD
-(void)turnPageForward
UIViewController * pageIndex =[pageArray objectAtIndex:nextPageNumber]; //nextPageNumber is the next page to load
[pageController setViewControllers:[NSArray arrayWithObject:pageIndex]
direction:UIPageViewControllerNavigationDirectionForward
animated:YES completion:^(BOOL finished)
];
pageIndex = nil;
我已经通过 *** 查看了使用向UIPageViewController
提供页面的相同方式的帖子,但没有找到任何接近的东西。最接近的是“ARC 在导航控制器中“返回”时没有释放内存”,但没有以相同的方式设置视图控制器。
我已尝试将不需要的页面设置为 nil,以便 ARC 可以在不走运的情况下删除它们。我应该尝试任何建议或替代路径吗?我喜欢卷页效果,但在其他地方找不到一个很好的水平卷页效果。
谢谢!埃里克
【问题讨论】:
【参考方案1】:“UIPageViewController 似乎将它实例化的每个页面都保存在内存中”
不,您是通过实例化所有这些“页面”(控制器)并将它们放入数组中来做到这一点的(当您翻页时内存会跳转,因为控制器的视图在您显示它之前实际上并没有加载,即使虽然控制器已经被实例化了。但是一旦你这样做了,你的控制器会保留它的视图并且数组会保留控制器)。你只需要保留一些你在哪个页面上的计数,并在 viewControllerBeforeViewController: 和 viewControllerAfterViewController: 的实现中,在那里实例化一个页面。当您离开该页面时,其控制器将被释放。如果加载速度很慢,您可能需要保留一个包含当前页面和之前和之后页面的数组——如果您将它设置为滚动而不是页面卷曲来进行转换,UIPageViewController 会这样做。
我做了一个这样的简单测试应用:
@implementation ViewController
int count;
- (void)viewDidLoad
[super viewDidLoad];
count = 1;
self.pager = [[UIPageViewController alloc]initWithTransitionStyle:UIPageViewControllerTransitionStylePageCurl navigationOrientation:UIPageViewControllerNavigationDirectionForward options:nil];
self.pager.dataSource = self;
self.pager.delegate = self;
Page1 *first = [[Page1 alloc] initWithNibName:@"Page1" bundle:nil];
[self.pager setViewControllers:@[first] direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
[self addChildViewController:self.pager];
[self.view addSubview:self.pager.view];
[self.pager didMoveToParentViewController:self];
-(UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
if (count > 1)
NSString *nibName = [@"Page" stringByAppendingFormat:@"%d",count-1];
UIViewController *prev = [[NSClassFromString(nibName) alloc] initWithNibName:nibName bundle:nil];
count -= 1;
return prev;
return nil;
-(UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
if (count < 3)
NSString *nibName = [@"Page" stringByAppendingFormat:@"%d",count+1];
UIViewController *next = [[NSClassFromString(nibName) alloc] initWithNibName:nibName bundle:nil];
count += 1;
return next;
return nil;
我的示例只有 3 页,但它说明了执行此操作的一种方法。我将日志放在 3 个控制器的 dealloc 方法中,当我离开它们时会调用它们。
【讨论】:
感谢您的帮助和友好的解释!这些页面现在调用 dealloc 就好了。不过,我确实有一个问题。内存仍然没有被释放。我应该在 dealloc 方法中单独释放对象吗?我还看到之后没有为任何页面调用 viewWillUnload 和 ViewDidUnload 。有什么想法吗?我很乐意进一步调查,但也许你有一条我可以研究的可能路径。谢谢埃里克 我很抱歉。我不是故意放 viewWillUnload (没有这样的东西)。此外,每个页面都会出现 viewWillDisappear 和 viewDidDisappear,只是不会出现 viewDidUnload。 @EricAdvance,你怎么知道内存没有被释放?如果您使用的是 ARC,它应该是。您不需要在 ARC 下释放 dealloc 中的视图,这已经为您完成了。您是否保留对这些对象的任何引用,或者它们都归视图所有?至于为什么不调用 viewWillDisappear,我不知道。 @EricAdvance,我刚刚测试了我的,viewWillDisappear 和 viewDidDisappear 都被调用了。 @EricAdvance,viewDidUnload 已被贬值,不再被调用。这通常在内存不足的情况下调用,在这种情况下,系统会在控制器还活着的时候释放这些视图的内存——这在你的情况下并不重要,因为你的控制器正在被释放,并且视图将是也是。【参考方案2】:我知道这个问题有点老了,但也许我的方法可以帮助一些在不使用 ARC 时面临同样问题的人。
我认为释放未使用页面的最佳方法是在 UIPageViewController 的 didFinishAnimation 方法中。您可以在那里获得 1 或 2 个以前的视图控制器,并且可以轻松地释放它们。我是这样做的:
-(void)pageViewController:(UIPageViewController *)thePageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed
if(completed)
for (UIViewController *viewController in previousViewControllers)
[viewController release];
只有在操作完成后才释放它们很重要,否则在下一个页面更改时,您将尝试访问已释放的页面。
希望这会有所帮助!
【讨论】:
【参考方案3】:我这次遇到了这个问题。 经过一个小时的思考,我找到了解决方案。
这是我的 .h 文件
#import <UIKit/UIKit.h>
@interface BKContentViewController : UIPageViewController
@end
还有我的 .m 文件和 viewDidload 方法
- (void)viewDidLoad
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
self.delegate = self;
self.dataSource = self;
[self setViewControllers:[NSArray arrayWithObject:detailContent]
direction:UIPageViewControllerNavigationDirectionForward
animated:NO
completion:NULL];
还有最重要的。
#pragma mark - UIPageViewControllerDataSource UIPageViewControllerDelegate
- (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed
if (completed)
[self setViewControllers:[NSArray arrayWithObject:detailContent]
direction:UIPageViewControllerNavigationDirectionForward
animated:NO
completion:NULL];
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController
viewControllerAfterViewController:(UIViewController *)viewController
if (!detailRight)
detailRight = [[DetailContent alloc] init];
[detailRight setShouldReturn:YES];
[detailRight setPath:fPath withFileName:fName];
return detailRight;
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController
viewControllerBeforeViewController:(UIViewController *)viewController
if (!detailLeft)
detailLeft = [[DetailContent alloc] init];
[detailLeft setShouldReturn:YES];
[detailLeft setPath:fPath withFileName:fName];
return detailLeft;
- (UIPageViewControllerSpineLocation)pageViewController:(UIPageViewController *)pageViewController
spineLocationForInterfaceOrientation:(UIInterfaceOrientation)orientation
UIViewController *currentViewController = [self.viewControllers objectAtIndex:0];
NSArray *viewControllers = [NSArray arrayWithObject:currentViewController];
[self setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:NULL];
self.doubleSided = NO;
return UIPageViewControllerSpineLocationMin;
在这之后,我只有三个viewContrller,并且一次又一次滚动不会增加内存。 希望这会对你有所帮助。
【讨论】:
以上是关于释放 UIPageViewController 中未使用的页面的主要内容,如果未能解决你的问题,请参考以下文章
uipageviewcontroller 中的 Swift uipageviewcontroller
UIPageViewController 背景在 Objective C iOS 中从不透明