xcode 释放 UIPageViewController 中未使用的页面
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14755782/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Releasing unused pages in UIPageViewController
提问by EricAdvance
I am using a separate .h
, .m
, and .xib
files for each UIViewController
based page of a UIPageViewController
based picture book. Each page is loaded with animations, music, etc. and takes about 4MB of memory. In Instruments, the free memory drops down about 4MB as each page is loaded. This memory is never released as the pages are turned. It eventually gives memory warnings. UIPageViewController
seems to keep each page it instantiates in memory and won't unload it. So when pages are turned fast, the app crashes.
我正在为基于图画书的每个页面使用单独的.h
、.m
和.xib
文件。每个页面都加载了动画、音乐等,占用了大约 4MB 的内存。在 Instruments 中,随着每个页面的加载,空闲内存下降了大约 4MB。当翻页时,这个内存永远不会被释放。它最终会发出内存警告。 似乎将它实例化的每个页面都保留在内存中,并且不会卸载它。因此,当页面快速翻动时,应用程序会崩溃。UIViewController
UIPageViewController
UIPageViewController
I would like to be able to unload all pages except the 3 needed by UIPageViewController
- the previous, current, and next pages. How can I unload undesired pages since they were instantiated by UIPageViewController
.
我希望能够卸载除UIPageViewController
上一页、当前页和下一页所需的 3 页之外的所有页面。我如何卸载不需要的页面,因为它们是由UIPageViewController
.
Below is the Array of the pages that UIPageViewController
pulls from. All of the pages (Page1, Page2, etc.) basically just load image files, provide basic animation, and have music.
下面是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];
I've left out the standard initialization for UIPageViewController
. It uses "nextPageNumber
" to pull the right page from the pageArray
above to create a new page object.
我省略了UIPageViewController
. 它使用“ nextPageNumber
”从pageArray
上面拉出正确的页面来创建一个新的页面对象。
-(void)turnPageForward{
[pageController setViewControllers:[NSArray arrayWithObject:[pageArray objectAtIndex:nextPageNumber]]
direction:UIPageViewControllerNavigationDirectionForward
animated:YES completion:^(BOOL finished){
}];
}
}
I have tried creating an object "pageIndex
" (see below) that is set to nil after providing it to the pageController
. It didn't work. The page still took up memory well after the pages had advanced.
我尝试创建一个对象“ pageIndex
”(见下文),在将其提供给pageController
. 它没有用。在页面前进后,页面仍然占用大量内存。
//PROGRAM PAGE FORWARD
-(void)turnPageForward{
-(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;
}
}
I've looked through stackoverflow for posts using the same way of supplying pages to UIPageViewController
, but haven't found anything close. The closest was "ARC not releasing memory when going “back” in navigation controller" but doesn't set the view controllers the same way.
我已经使用相同的方式为 提供页面查看了 stackoverflow 中的帖子UIPageViewController
,但没有找到任何接近的内容。最接近的是“ ARC 在导航控制器中“返回”时不会释放内存”,但不会以相同的方式设置视图控制器。
I've tried to set the undesired pages to nil so ARC can remove them with no luck. Any suggestions or alternate paths I should try? I like the page curl effect and have not been able to find a good one elsewhere that does horizontal page curls.
我试图将不需要的页面设置为 nil,这样 ARC 就可以毫无运气地删除它们。我应该尝试任何建议或替代路径?我喜欢页面卷曲效果,并且在其他地方找不到可以进行水平页面卷曲的效果。
Thanks! Eric
谢谢!埃里克
回答by rdelmar
"UIPageViewController seems to keep each page it instantiates in memory"
“UIPageViewController 似乎将它实例化的每个页面都保存在内存中”
No, you're doing that by instantiating all those "pages" (controllers), and putting them into an array (the memory jumps as you turn the page because the controller's view is not actually loaded until you display it, even though the controller has been instantiated. But once you've done that, your controller retains its view and the array retains the controller). You just need to keep some kind of count of which page you're on, and in the implementation of viewControllerBeforeViewController: and viewControllerAfterViewController:, instantiate a page there. When you go away from that page, its controller will be dealloc'd. If the loading is slow, you might need to keep an array that has the current page and the ones before and after -- UIPageViewController does do that if you have it set to scroll instead of page curl for the transition.
不,您是通过实例化所有这些“页面”(控制器)并将它们放入一个数组中(内存会在您翻页时跳转,因为控制器的视图直到您显示它才实际加载,即使控制器已经被实例化。但是一旦你这样做了,你的控制器就会保留它的视图,而数组会保留控制器)。您只需要对您所在的页面进行某种计数,并在 viewControllerBeforeViewController: 和 viewControllerAfterViewController: 的实现中,在那里实例化一个页面。当您离开该页面时,其控制器将被解除分配。如果加载缓慢,
I made a simple test app like this:
我制作了一个简单的测试应用程序,如下所示:
@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;
}
My example only has 3 pages, but it illustrates one way to do this. I put logs in the dealloc methods of the 3 controllers, and they were called when I navigated away from them.
我的示例只有 3 页,但它说明了一种方法。我将日志放在 3 个控制器的 dealloc 方法中,当我离开它们时会调用它们。
回答by Philipp Otto
I know this question is a bit older, but maybe my approach can help some people facing the same issue when not using ARC.
我知道这个问题有点老了,但也许我的方法可以帮助一些在不使用 ARC 时面临同样问题的人。
I think the best way to release unused pages is in the didFinishAnimation method of the UIPageViewController. You get the 1 or 2 previous view controllers there and you can easily release them. I do it like this:
我认为释放未使用页面的最佳方法是在 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];
}
}
}
It is important that you only release them if the action was completed because otherwise with the next page change you would try to access released pages.
只有在操作完成后才释放它们很重要,否则在下一页更改时您将尝试访问已释放的页面。
Hope this helps!
希望这可以帮助!
回答by Dzy
I encounter this problem this time. And after an hour thinking,I had find a solution.
这次我遇到了这个问题。经过一个小时的思考,我找到了解决方案。
This is my .h file
这是我的 .h 文件
#import <UIKit/UIKit.h>
@interface BKContentViewController : UIPageViewController
@end
And my .m file and viewDidload method
还有我的 .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];
}
And the most important.
而最重要的。
#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;
}
After this, I have only three viewContrller and the memory will not increase by scroll again and again. Hope this will help you.
在此之后,我只有三个viewContrller,并且一次又一次地滚动不会增加内存。希望这会帮助你。