This is not really a question about addressing a specific problem but more a request to be pointed in the right direction.
I am making an app where I am loading several images (saved as JPGs) onto a screen at the same time and this has to be the way it is, I can't unload any of them because they are all being shown at once.
I tried loading 30 images at about 800px*600px resolution naively thinking that it only loads the 'compressed' size of the pictures into memory (this being about 200 KB) - so a total of 6 MB? Of course, several memory warnings later, I realised how stupid I was. So i now re-size them to about 400px*300px each and my iPhone 4S just about copes with the memory requirements.
I originally used a UiView where in the drawRect I drew a custom drawn image but changing over to using a UIImageView improved the situation dramatically. The app is so much faster and responsive.
I also found that switching off rasterization in my layer made a huge difference in performance.
These sorts of 'discoveries' have taken me hours to work out and what I was wondering was if there are particular design patterns or resources that I can use to load my images to the screen as efficiently as possible; mainly regarding using as little memory as possible - are there are good rules of thumb? Why did using UIImageView vs UiView w/ image make such a difference?
Would be very grateful if someone could help.
Thanks.
Implementing -drawRect:
causes the system to allocate a bitmap image of the same size as your view (after all, you need a buffer to draw in to). If all you're doing is drawing an image you've already loaded, then in one fell swoop you've doubled the memory usage for that image (because you have the copy you loaded, and the second copy you just drew).
Similarly, rasterizing layers requires allocating bitmap images the same size as the layer, so it has a buffer to rasterize into. So turning that on sucks up memory as well (proportional to the size of the layer).
The basic rule of thumb is, don't do extra work. Using -drawRect:
to draw an image is extra work. Rasterizing a layer is extra work (though, depending on what the layer is, this may be a one-time performance cost (and a constant memory cost) in order to save on performance later, e.g. if it's a CAShapeLayer
or if it's drawing shadows). Keeping large images in memory that you always scale down before rendering to screen is extra work (just scale it down once when you load the image, and keep the scaled copy around).
Another thing to keep in mind is, if your goal is to draw images, you should try to use UIImageView
if you possibly can. It's generally the fastest and cheapest way to get an image to the screen, and it's reasonably flexible.
The design pattern is basically, use UIImageView
. Apple has spent a lot of time making it fast, and Apple is allowed to use private APIs that you aren't.
That said, if you want to do it yourself, you should try using one CALayer
per image. You just load an image and set it as the content
property of a CALayer
. The CALayer
can cache its contents in GPU memory, and may do other optimizations that you can't do with public APIs.
You can learn a lot about making your UI fast by watching Apple's development videos. They include a lot of tips and "inside info" that are either not in the written documentation, or hard to find/easy to overlook in the docs. The development videos are here: http://developer.apple.com/videos/. Some good ones relevant to your question:
- iOS - "Understanding iOS View Compositing"
- WWDC 2011 - "Understanding UIKit Rendering"
- WWDC 2011 - "Practical Drawing for iOS Developers"
- WWDC 2011 - "Core Animation Essentials"
- WWDC 2010 - "Core Animation in Practice"