54954

Releasing memory of multiple view controllers inside a scrollview

Question:

I have an app the loaded many view controllers in a scroll view depending on the number of objects the user has in a tableview. So when I flip between the tableview and the scroll view, the number of view controllers in the scroll view changes according to how many objects the user has in the tableview.

I use the code in Apple's PageControl sample code to build the scroll view with many view controllers inside it, after some modification of course.

- (void)loadScrollViewWithPage:(int)page { if (page < 0) return; if (page >= kNumberOfPages) return; // replace the placeholder if necessary MainViewController *countdownController = [viewControllers objectAtIndex:page]; if ((NSNull *)countdownController == [NSNull null]) { id occasion = [eventsArray objectAtIndex:page]; countdownController = [[MainViewController alloc] initWithPageNumber:page]; [countdownController setOccasion:occasion]; [viewControllers replaceObjectAtIndex:page withObject:countdownController]; [countdownController release]; } // add the controller's view to the scroll view if (nil == countdownController.view.superview) { CGRect frame = scrollView.frame; frame.origin.x = frame.size.width * page; frame.origin.y = 0; countdownController.view.frame = frame; [scrollView addSubview:countdownController.view]; } }

The problem is the number of living view controllers (MainViewController here) keeps increasing when I flip between the table view and the scroll view (according to Instruments) even though I didn't add any new objects which causes memory problems of course.

I tried so many things in viewWillDisappear of the scroll view like:

- (void) viewWillDisappear:(BOOL)animated { //test unloading all views //Remove all subviews [[scrollView subviews] makeObjectsPerformSelector:@selector(removeFromSuperview)]; //[[scrollView subviews] makeObjectsPerformSelector:@selector(release)]; //[viewControllers removeAllObjects]; for (unsigned m = 0; m < [viewControllers count]; m++) { //[[viewControllers objectAtIndex:m] makeObjectsPerformSelector:@selector(release)]; [viewControllers removeObjectAtIndex:m]; } }

But it didn't work. Here is a recording of how the app works youtube.com/watch?v=5W8v_smZSog

And this is the viewWillAppear method of the scroll view:

- (void)viewWillAppear:(BOOL)animated { eventsArray = [[NSMutableArray alloc] init]; kNumberOfPages = [self.dataModel occasionCount]; //update the eventsArray from the dataModel //Fill in the events Array with occasions form the data model for (unsigned r = 0; r < kNumberOfPages; r++) { Occasion* occasion = [self.dataModel occasionAtIndex:r]; [eventsArray insertObject:occasion atIndex:r]; } // view controllers are created lazily // in the meantime, load the array with placeholders which will be replaced on demand NSMutableArray *controllers = [[NSMutableArray alloc] init]; for (unsigned i = 0; i < kNumberOfPages; i++) { [controllers addObject:[NSNull null]]; } self.viewControllers = controllers; [controllers release]; // a page is the width of the scroll view scrollView.pagingEnabled = YES; scrollView.contentSize = CGSizeMake(scrollView.frame.size.width * kNumberOfPages, scrollView.frame.size.height); scrollView.showsHorizontalScrollIndicator = NO; scrollView.showsVerticalScrollIndicator = NO; scrollView.scrollsToTop = NO; scrollView.delegate = self; pageControl.numberOfPages = kNumberOfPages; pageControl.currentPage = currentPage; [self loadScrollViewWithPage:0]; [self loadScrollViewWithPage:1]; }

UPDATE: Video recording of Instruments <a href="http://www.youtube.com/watch?v=u1Rd2clvMQE&feature=youtube_gdata_player" rel="nofollow">http://www.youtube.com/watch?v=u1Rd2clvMQE&feature=youtube_gdata_player</a>

And a screen shot showing the responsible caller: <img alt="enter image description here" class="b-lazy" data-src="https://i.stack.imgur.com/WyckN.png" data-original="https://i.stack.imgur.com/WyckN.png" src="https://etrip.eimg.top/images/2019/05/07/timg.gif" />

Thank you.

Answer1:

This is for you if you don't want to use UIPageViewController (read my other answer).

The sample project is designed for a constant number of pages (kNumberOfPages). The scrollview content size and the size of the view controller array depends on the number of pages. The sample code set this up in awakeFromNib, which is called only once.

So in order to make this dynamic you could recreate the whole ContentController when the number of pages changes. You just need to add a property for the number of pages.

The other option would be to reset the scrollview and view controller array when the number of pages changes.

I'm assuming you have defined a property for the events:

@property(nonatomic,retain) NSArray* eventsArray;

You could then add a setter method like this:

-(void)setEventsArray:(NSArray *)eventsArray { if (eventsArray != _eventsArray) { [_eventsArray release]; _eventsArray = [eventsArray retain]; NSUInteger eventCount = [eventsArray count]; //reset scrollview contentSize scrollView.contentSize = CGSizeMake(scrollView.frame.size.width * eventCount, scrollView.frame.size.height); // reset content offset to zero scrollView.contentOffset = CGPointZero; //remove all subviews [[scrollView subviews] makeObjectsPerformSelector:@selector(removeFromSuperview)]; pageControl.numberOfPages = eventCount; // reset viewcontroller array NSMutableArray *controllers = [[NSMutableArray alloc] init]; for (unsigned i = 0; i < eventCount; i++) { [controllers addObject:[NSNull null]]; } self.viewControllers = controllers; [controllers release]; [self loadScrollViewWithPage:0]; [self loadScrollViewWithPage:1]; } }

You call this method from the table view controller at the time when the user switches to the scroll view.

Answer2:

Apple's PageControl sample code is 2 years old and you can consider it as deprecated because there is a new container view controller in iOS 5 that does all this: UIPageViewController.

You should really start using UIPageViewController, then you don't need that loadScrollViewWithPage method at all. It would be less code and more easy.

Take a look at the <a href="http://developer.apple.com/library/ios/#samplecode/PhotoScroller" rel="nofollow">PhotoScroller</a> sample code. It has been updated to take full advantage of UIPageViewController.

Answer3:

It doesn't look like you are implementing Apple's <a href="http://developer.apple.com/library/ios/#featuredarticles/ViewControllerPGforiPhoneOS/CreatingCustomContainerViewControllers/CreatingCustomContainerViewControllers.html#//apple_ref/doc/uid/TP40007457-CH18-SW6" rel="nofollow">View Controller Containment</a> pratices. It would make memory management that much easier and safer.

Plus, hoping that it might save you a lot of future headaches, there is already an open source project that does what you are describing (implementing a self-managing scrollview of an arbritary number of view controllers).

You might want to take a look at it: <a href="https://github.com/heardrwt/RHHorizontalSwipe" rel="nofollow">RHHorizontalSwipe</a>.

Answer4:

The concept of a UIScrollView containing multiple UIViewController views sounds sketchy at best, that design does not sound good at all.

That being said, one potential issue could be this line:

if ((NSNull *)countdownController == [NSNull null])

You would be better off with something like this:

if (!countdownController || [countdownController isKindOfClass:[NSNull class]])

Also, you should call [super viewWillDisappear:animated] in your viewWillDisappear method.

Recommend

  • Stop NSThread on iOS
  • NSXMLParserErrorDomain error 31
  • Search from NSArray using Predicate
  • viewDidAppear & viewWillAppear not firing in tabbar app
  • ipad objective c using removeFromSuperview to remove UICollectionViewController throws an error
  • Swapping between UIViews in one UIViewController
  • How to make the NSButton above a NSButton?
  • Releasing memory of multiple view controllers inside a scrollview
  • Is it possible to customize UITabBarItem Badge?
  • change alpha of a child view without changing alpha of parent view
  • How to implement sections in table view containing SQLite database?
  • Multiple lines of text in UISegmentedControl
  • -JSONValue Failed error trace is:Garbage after JSON
  • UITableViewCell hide separator using separatorInset fails in iOS 11
  • Localization for UITextView in storyboard
  • To move to next text fieds in table cell
  • Core Data bindings with subviews and multiple NIBs
  • iPhone viewDidAppear stops firing after loading/dismissing a modal view
  • UIImage animating an array of images
  • How to render a collection of different Marionette Views
  • Tableview button selection wrong after search
  • Swift: How can I detect elements inside of a UIView?
  • UIView to stick at bottom of UIScrollView
  • UIView alpha 0 but still receive touch events?
  • Capturing image of UIView (renderInContext not working properly) [closed]
  • Overreleasing here?
  • How to programmatically dismiss system dialogs like “ would like to access your photos”?
  • ember js subviews and didinsertelement event
  • Simple Angular 2 app gives “Potentially unhandled rejection” error
  • How to click on a link that has a certain content in puppeteer?
  • How to make the tableview response pan gesture in ZUUIRevealController
  • How to replace TouchesBegan with UIGestureRecognizer
  • how to remove a div with same ids but display='block' and display='none' in JAVa
  • Jquery popup on mouse over of calendar control
  • QLPreviewController hide print button in ios6
  • With Hadoop, can I create a tasktracker on a machine that isn't running a datanode?
  • Regex thinks I'm nesting, but I'm not
  • What is the “return” in scheme?
  • How to disable jQuery.jplayer autoplay?
  • Observable and ngFor in Angular 2