Optimizing the page

I am developing for iPhone, SDK 3.1. I have about 150 images that I need to show to the user in order for them to view the pages. I copied the code from Apple's PageControl sample, but once I load it on the iPhone, the app crashes if I scroll quickly. I tried to write some optimizations to save memory, but it doesn't help much. I was wondering if someone could tell me if my optimization is in need or if I have another problem. The relevant code is below.

// ReviewViewController.h
@interface ReviewViewController : UIViewController <UIActionSheetDelegate, UIScrollViewDelegate> {

 NSMutableArray *reviewArr;
    NSMutableArray *viewControllers;
 IBOutlet UIScrollView *scroller;
 BOOL dirty;
 NSInteger pageCount;
}

@property (retain, nonatomic) NSMutableArray *reviewArr;
@property (retain, nonatomic) NSMutableArray *viewControllers;
@property (assign, nonatomic) UIScrollView *scroller;


//ReviewViewController.m

-(void)clearScroller {
 NSArray *subviews = [[NSArray alloc] initWithArray:scroller.subviews];
 for (UIView *subview in subviews) {
  //NSLog(@"DEBUG - view %d", subview.tag);
  [subview removeFromSuperview];
 }
 [subviews release];
 [scroller setContentOffset:CGPointMake(0,0) animated:NO]; 
}

- (void)initialize:(int)page {
 if([reviewArr count] == 0) {
  NSLog(@"No more cards on stack");
  UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(20, 70, 420, 45)];
  label.text = @"You have no review cards in your stack";
  label.numberOfLines = 2;
  label.font = [UIFont systemFontOfSize:20];
  [scroller addSubview:label];
  [label release];
  [back setHidden:YES];
  [forward setHidden:YES];
  [mathFactAction setHidden:YES];
  return;
 } 

 NSLog(@"Initializing..%d", page);

 [self setHeading];
 [self clearScroller];
 [mathFactAction setHidden:NO];

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

 // a page is the width of the scroll view
 scroller.pagingEnabled = YES;
 scroller.contentSize = CGSizeMake(scroller.frame.size.width * reviewArr.count, scroller.frame.size.height);
 scroller.showsHorizontalScrollIndicator = NO;
 scroller.showsVerticalScrollIndicator = NO;
 scroller.scrollsToTop = NO;
 scroller.delegate = self;

 // pages are created on demand
 // load the visible page
 // load the page on either side to avoid flashes when the user starts scrolling
 if(page > 0) {
  [self loadScrollViewWithPage:page - 1];
 }

 [self loadScrollViewWithPage:page];
 [self loadScrollViewWithPage:page + 1]; 
}

- (void)checkForDirtyPages {
 for (unsigned i = 0; i < [self.viewControllers count]; i++) {
  if(i == pageCount-1) continue;
  else if(i == pageCount) continue;
  else if(i == pageCount+1) continue;
  else unloadPage:i;
 } 
} 

- (void)unloadPages {
 dirty = FALSE;
 [self unloadPage:pageCount-3];
 [self unloadPage:pageCount-4];
 [self unloadPage:pageCount+3];
 [self unloadPage:pageCount+4];
}

- (void)unloadPage:(int)page {
    if (page < 0) return;
    if (page >= reviewArr.count) return;

    // replace the placeholder if necessary
    FactViewController *controller = [viewControllers objectAtIndex:page];
    if ((NSNull *)controller != [NSNull null]) {

  /*NSArray *subviews = [[NSArray alloc] initWithArray:controller.view.subviews];
  for (UIView *subview in subviews) {
   //NSLog(@"DEBUG - view %d", subview.tag);
   [subview removeFromSuperview];
  }
  [subviews release]; */
  // remove teh innerscroller from viewControllers to conserve memory
  [self.viewControllers replaceObjectAtIndex:page withObject:[NSNull null]];
 }
}

- (void)loadScrollViewWithPage:(int)page {
    if (page < 0) return;
    if (page >= reviewArr.count) return;

    // replace the placeholder if necessary
    FactViewController *controller = [viewControllers objectAtIndex:page];
 //UIScrollView *innerScroller = [self.viewControllers objectAtIndex:page];
    if ((NSNull *)controller == [NSNull null]) {
        controller = [[FactViewController alloc] initWithReviewNumber:[reviewArr objectAtIndex:page]];
        [self.viewControllers replaceObjectAtIndex:page withObject:controller];
  [controller release];
 }

 if (nil == controller.view.superview) {
        CGRect frame = scroller.frame;
        frame.origin.x = frame.size.width * page;
        frame.origin.y = 0;
        controller.view.frame = frame;
        [scroller addSubview:controller.view];
  //[self unloadPages];
 }

}

- (void)scrollViewDidEndDecelerating:(UIScrollView *)sender {
 if(dirty) [self unloadPages];
 //[self resetScroller:pageCount-1];
 //[self resetScroller:pageCount+1];
}

- (void)scrollViewDidScroll:(UIScrollView *)sender {
    // We don't want a "feedback loop" between the UIPageControl and the scroll delegate in
    // which a scroll event generated from the user hitting the page control triggers updates from
    // the delegate method. We use a boolean to disable the delegate logic when the page control is used.
    // Switch the indicator when more than 50% of the previous/next page is visible
    CGFloat pageWidth = scroller.frame.size.width;
    int page = floor((scroller.contentOffset.x - pageWidth / 2) / pageWidth) + 1;
   // pageControl.currentPage = page;

    // load the visible page and the page on either side of it (to avoid flashes when the user starts scrolling)
    [self loadScrollViewWithPage:page - 1];
    [self loadScrollViewWithPage:page];
    [self loadScrollViewWithPage:page + 1];

 // if we are on a new page
 if(page != pageCount) {
  dirty = TRUE;
  pageCount = page;
  [self setHeading];
 }

    // A possible optimization would be to unload the views+controllers which are no longer visible
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview
    // Release anything that not essential, such as cached data
 [self checkForDirtyPages];
}

// FactViewController.m

@interface FactViewController : UIViewController {
 IBOutlet UIScrollView *scrollView; 
 //IBOutlet UILabel *pageNumberLabel;
    NSInteger review_id;
}

@property (nonatomic, retain) UIView *scrollView;
//@property (nonatomic, retain) UILabel *pageNumberLabel;
@property (assign, nonatomic) NSInteger review_id;

-(void)unloadImage;
- (id)initWithReviewNumber:(NSNumber *)reviewNumber;
- (int)getMathFactId;

@end

// Load the view nib and initialize the pageNumber ivar.
- (id)initWithReviewNumber:(NSNumber *)reviewNumber {
    if (self = [super initWithNibName:@"FactViewController" bundle:nil]) {
        self.review_id = [reviewNumber intValue];
    }
    return self;
}

- (void)dealloc {
    //[pageNumberLabel release];
 //[card release];
 [scrollView release];
    [super dealloc];
}

// Set the label and background color when the view has finished loading.
- (void)viewDidLoad {
 NSLog(@"(FactVC) view did load");
 [self showImage];
 [super viewDidLoad];

}

-(void)unloadImage {
 [scrollView release];
}

-(void)showImage {
 NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
 [numberFormatter setMinimumIntegerDigits:2];
 NSLog(@"Loading image (%d)",  self.review_id);

 NSString *img_file = [NSString stringWithFormat:@"%@", [numberFormatter stringForObjectValue:[NSNumber numberWithInt:self.review_id]]];
 [numberFormatter release];

 NSString *fileLocation = [[NSBundle mainBundle] pathForResource:img_file ofType:@"gif"];
 NSData *imageData = [NSData dataWithContentsOfFile:fileLocation];

 UIImage *image = [UIImage imageWithData:imageData];

 //UIImage *image = [[UIImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:img_file ofType:@"gif"]];
 UIImageView *imageView = [[UIImageView alloc] initWithImage:image]; // inDirectory:@"Resources/questions"

 [scrollView addSubview:imageView];
 [scrollView setContentSize:CGSizeMake(imageView.frame.size.width, imageView.frame.size.height)];

 [imageView release];
}

      

Too bad it took so long, but I wanted to make sure it was all there.

+2


source to share





All Articles