views:

361

answers:

3

I have a problem with removing unused pages from an array:

NSMutableArray *controllers = [[NSMutableArray alloc] init];
for (unsigned i = 0; i < [descriptionsList count]; i++) {
        [controllers addObject:[NSNull null]];
}
self.viewControllers = controllers;
[controllers release];
[self loadScrollViewWithPage:0];
[self loadScrollViewWithPage:1];

i'm adding objects with:

- (void)loadScrollViewWithPage:(int)page {
    if (page < 0) return;
    if (page >= [descriptionsList count]) return;
    // replace the placeholder if necessary
    DetailsView *controller = [viewControllers objectAtIndex:page];
    if ((NSNull *)controller == [NSNull null]) {
        controller = [[DetailsView alloc] initWithElement:[descriptionsList objectAtIndex:page]
                andFrame:CGRectMake(320*page, 0, 320, 420)];
        [viewControllers replaceObjectAtIndex:page withObject:controller];
        [controller release];
    }

    // add the controller's view to the scroll view
    if (nil == controller.superview) {
        [scrollView addSubview:controller];
    }
}

and i'm using this to remove and create pages:

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    pageControlUsed = NO;

    //load the visible page and the page on either side of it (to avoid flashes when the user starts scrolling)
    for (unsigned i = 0; i < [descriptionsList count]; i++) {
     if (i < pageController.currentPage - 1 || i > pageController.currentPage + 1) {
      if ([viewControllers objectAtIndex:i] != nil) {
       [[viewControllers objectAtIndex:i] release];
       [viewControllers replaceObjectAtIndex:i withObject:[NSNull null]];
      }
     }
     else {
      [self loadScrollViewWithPage:i];
     }
    }
}

My app is crashing big time when i want to view page 3. Any advice on how should this be done? Thanks.

+2  A: 

A couple of problems:

  • NSArrays can't store 'nil' objects, so your check for != nil will always succeed, so you don't need it
  • You definitely should not be releasing the object in the array; You don't have a corresponding -retain message, and regardless, the array will automatically retain objects put into it, and release them when they're removed
  • your nomenclature is a little confusing. You have an array called viewControllers, and an objected called controller, but these both appear to be views (since you're initWithFrame'ing them.
Ben Gottlieb
+1  A: 

This line:

if ([viewControllers objectAtIndex:i] != nil)

will always evaluate as TRUE because the array is populated with NSNULL objects which do not evaluate to nil. The block executes even when there is a view stored at index. This block will populate your entire array with NSNull objects, wiping out all your views. Any subsequent call to the view will crash.

I think you've got a bad design here. You shouldn't be putting views into an Array. Instead, you need to have your data in an array and then populate reusable views based on what data should be displayed at any given time. Look at how 'UITable' displays itself with reusable 'UITableViewCells'.

TechZen
A: 

This approach with lazy loading i've got it from a good book in fact, but the sample was with very very simple views, and without releasing them. The pages initialization was made in the scrollViewDidScroll method, which was a total mess on the device with my content: a photo and 2 texts. And the memory used is crashing my app, that's why i want to keep loaded only 3 pages. Here's the updated code, but i can't release the object nor remove from view, so i get duplicates.

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {

    for (unsigned i = 0; i < [descriptionsList count]; i++) {
     if (i < pageController.currentPage - 1 || i > pageController.currentPage + 1) {
      DetailsView *controller = [viewControllers objectAtIndex:i];
      if ((NSNull *)controller != [NSNull null]) {
       if (nil != controller.superview) {
        NSLog(@"remove from superview %d", i);
        //[controller.superview removeFromSuperview];
       }
       [viewControllers removeObjectAtIndex:i];
       [viewControllers insertObject:[NSNull null] atIndex:i];
       //[viewControllers replaceObjectAtIndex:i withObject:[NSNull null]];
      }
     }
     else {
      NSLog(@"allocating %d", i);
      [self loadScrollViewWithPage:i];
     }
    }
}

So, will i be able to create my views in real time without flashes if i'm using only 2 reusable views? I've saw a sample with 2 views but said that the content must exist, and i'm not sure how good is to keep in memory about 15 pngs.

Cristi Băluță