views:

69

answers:

4

I have kind of a unique problem in the project I'm working in. What I'm doing is creating sort of a "wall" of scrollable images that are downloaded from a server that our user can flick through on the iPhone. But the problem is we're having trouble coming up with a good memory management plan here. What we have running right now is a class that subclasses UIImageView that is being used to store every image we download from the server (these images are saved to the app's documents directory though), and then we also add meta data to it (i.e. description, tags, etc). We are then storing this in an NSMutableArray that we then loop through with all the images to place them in our wall. As you would imagine having all these images in memory is an enormous memory hog. We are having a hard time thinking of a way to do this without using this much of memory. One of the bigger problems that makes it more difficult is that the images, when placed, need to report when being touched. So I don't think we can just deallocate them after placing them. Have any of you had experience with handling objects of any kind with large size like this and having to do have them at hand to draw to the screen, play, etc? What type of technique or approach do you suggest? I've read around about CoreData but don't know if this is what I'm looking for. Appreciate any help.

Here's the main drawing method for reference:

SOLUTION: WWDC Session Video #104!

A: 

For one, resample them down to a reasonable size -- presumably, you only need them to be about the screen's size or smaller. If you zoom in, go get the original for just that image.

Also, make a custom view (subclass of UIView) that draws the image (in drawRect) when it's visible and unloads the image when it's off-screen.

Lou Franco
I wouldn't resample them yourself on the phone, unless you first verify that it improves overall performance of your app. `UIImage` is quite good at these things, hook up Instruments and you will see.
mvds
+1  A: 

As long as the images come from disk, and read in using one of the UIImage convenience methods, the UIImage will remain backed by the file, and release memory when needed. It will re-read the file when needed as well. This is all just in the UIImage docs btw.

ps. even when loading an image from the iPhone library, it remains backed by the file (which your app cannot touch). The low-res version needed for display is kept as well btw. You can clearly see this happening in Instruments. An open question of me is if this is if camera images are backed by a file as well.

mvds
Hmm Yeah well what we're doing now basically is we have a queue of, say, 10 images. We download these synchronously and as each one finishes we add it to an NSMutableArray. We then loop through that array and add the images as a subview to a scrollview we have. In Instruments I'm showing up to 160mb being allocated in memory! So maybe we're doing something wrong but can't think of a way to have them report touches and not use that much memory at the same time.
Alexander
What you need to do is *save* them first, and then instantiate the `UIImage` from the disk. I got the simulator up to 120 mb on *one* big jpeg... So what? ;-)
mvds
the point is, that the memory usage reported in Instruments is partly (*mostly*, in this case) cached data, which vanishes in the blink of an eye.
mvds
Oh yes and another very important point: watch out for peak usage. I had a situation in which I had to convert a big image in several steps. This all revolved around autoreleased objects, meaning they kept building up before the conversion was complete. So instantiate a local `NSAutoReleasePool` wherever needed, especially when doing things in a loop!
mvds
+1  A: 

So really, what you need is a system of enqueuing and dequeuing views as they come and go.

An easy example you should be looking at is the Photos app that already exists on your iPhone. Although you might have a million images on your "wall", you can only see so many images at one time on your screen. That means you don't need a million UIImageViews for all of the images on the wall, just the 20 or so that fit onto the screen.

Think of the wall not as a single big view that's available all the time, but instead follow the paradigm laid out by UITableView. A UITableView enqueues UITableViewCells as they disappear and (hopefully) the datasource dequeues and reuses those table cells by just changing the information displayed to the one in the new row.

You shouldn't have any problems reporting touches on which image is touched (since you can just look at what image is contained in the imageview, or through some other saved property).

The above principles should also apply to the UIImages themselves; since you're saving them to disk, you don't necessarily need them all in memory, so think about releasing and getting them on demand. One thing to note: Sometimes scrolling can get pretty slow when you're reading images from disk, so it's a tradeoff you'll need to investigate for yourself.

David Liu
+1  A: 

This has basically been solved for you by David Golighty. We've used the ImageCache code successfully for 600+ images without a hiccup. Because of the way the caching works, we easily flip through 300 512x512 PNG images on both the iPhone and iPad with high frames-per-second.

Daniel Blezek
Thanks for that! I'm going to look at that now! :D
Alexander