0
votes

I've read a lot of UIScrollView with UIImageView threads here or other googled pages. But I still cannot get the problem I'm confronting. I'm having a cold right now. Hope I can still make it clear, lol. Here is the problem:

I'm building one app which mainly uses UIScrollView to display a few images. Here the amount counts, not the size, which is averagely 100KB(I even converted PNG to jpg, not sure whether it helps or not). With no more than 10 images, my app crashes with memory warning. This is the first time I encounter memory issue, which surprised me as the compiled app is less than 10MB.

At the very beginning, I load all the images on launch, looping all names of image files and do


    UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:imgName]];
    [scrollview addSubview:imageView];
    [imageView release];

If I'm right, I think after launch, all the images are in memory, right? But the funny thing here is, the app could launch without any problem(at most a level 1 memory warning). After I scroll a few images, it crashed. I checked leaks for sure and also allocations. No leak and allocation almost had no change during scrolling.

So, is there something special done by imageNamed rather than cache?

And then, yes, I turned to lazy load.

For fear of checking page and loading images on demand might jerk the scrolling(which was proved true), I used a thread which runs a loop to check offset of the scroll view and load/unload images.

I subclassed UIImageView by remembering the image name. It also contains loadImage and unloadImage which will be executed on that thread.


- (void)loadImage {
    /if ([self.subviews count] == 0) {
        UIImageView iv = [[UIImageView alloc] initWithImage:[UIImage imageNamed:self.imageName]];
        [self performSelectorOnMainThread:@selector(renderImage:) withObject:iv waitUntilDone:NO];
        //[self addSubview:iv];
        [iv release];
    }*/

if (self.image == nil) {
    //UIImage *img = [UIImage imageNamed:self.imageName];
    UIImage *img = [[UIImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:[self.imageName stringByDeletingPathExtension] ofType:[self.imageName pathExtension]]];
    // image must be set on main thread as UI rendering is main thread's responsibility
    [self performSelectorOnMainThread:@selector(renderImage:) withObject:img waitUntilDone:NO];
    [img release];
}

}

// render image on main thread - (void)renderImage:(UIImage*)iv { //[self addSubview:iv]; self.image = iv; }

  • (void)unloadImage { self.image = nil; //[(UIView*)[self.subviews lastObject] removeFromSuperview]; }

You can see the commented code that I've played with.

In unloadImage, if I write [self.image release], then I get EXC_BAD_ACCESS, which is unexpected, as I think alloc and release are matched here.

The app still crashes with no leak. The initWithContentsOfFile version even crashed earlier than imageNamed version, and made the scrolling not that smooth.

I run the app on device. By checking allocations, I found imageNamed version used much less memory than initWithContentsOfFile version, though they both crash. Instruments also showed that the allocated images were 2,3 or 4, which indicated the lazy load did do his job.

I checked PhotoScroller of WWDC2010, but I don't think it solvs my problem. There is no zooming or huge picture involved.

Anybody helps! Thank you in advance.

3

3 Answers

0
votes

The crash log says nothing. The app crashes mostly after memory warning level = 2. And if run on simulator, there will be no problem.

0
votes

It doesn't matter which format do you use for your images. They're converted to bitmaps when you display them.

I'd suggest to use the technique similar to that one which is used by UITableView (hide the image and free the memory it uses when it disappears from the screen and instantiate the image only when you need to show it).

As an alternate way – if you need to show these images in a grid – you might take a look to a CATiledLayer.

Anyhow, loading all the images to the memory is not the best idea :)

0
votes

You can load all the images to an array. And you can design a view having one image view and try the below code:

array name: examplearray and view name :exampleview

-(void)updateImagesInScrollView
{



    int cnt = [examplearray count];

    for(int j=0; j< cnt; ++j)
    {
        NSArray *nibContents = [[NSBundle mainBundle] loadNibNamed:@"exampleview" 
                                                             owner:self 
                                                           options:nil];

        UIView *myView = [nibContents objectAtIndex:0];
        exampleview * rview= (exampleview *)myView;
        /* you can get your iamge from the array and set the image*/ 
        rview.imageview.image = yourimage;
        /*adding the view to your scrollview*/
        [self.ScrollView addSubview:rview];
    }
    /* you need to set  the content size of the scroll view */
    self.ScrollView.contentSize = CGSizeMake(X, self.mHorizontalScrollView.contentSize.height);
}