iPhone memory management, making my app crashproof for multiple devices - iphone

I'm developing an app which uses a UIScrollView to show a list of images based on search criteria. Using a button the user can load more images.
When testing on an iPhone 4 the ViewController receives a memory-warning at ~750 images. When testing on an iPod 2nd generation a memory warning is received at ~150 images.
My understanding is that when didReceiveMemoryWarning is called, one can free memory by releasing objects but recovery from low memory is not guaranteed.
I've implemented didReceiveMemoryWarning and release basically all objects. In instruments I see memory usage drop back to ~3MB. The first time the iPod reaches its memory limit all goes well, memory is released and the app resumes normal operation. The second time however, when didReceiveMemoryWarning is called, I can see the objects released, but the app crashes anyway.
So, how do I make my app crash proof? I want to make sure that all devices running the app can load as much images as memory allows but I also want to make sure that the app doesn't crash.
I would prefer the app never to reach didReceiveMemoryWarning and set a limit to the number of images that can be displayed, but how can I determine how many images each possible device should be able to load?
Furthermore, the size of the images is not guaranteed. While testing I come to this arbitrary number of 150 on an iPod, but what if the images on the server at some point in time are twice as big? Then the app would probably crash at 75 images.
Any sugestions?

First off, what you probably want to do is not display all your images all at once. You rather, probably only want to disable the images that are currently visible, plus a few that are off screen preloaded for when the user scrolls to that location.
This is much the same way as the photos app works, how UITableView is implemented. Basically it boils down to this:
You have your main scrollview, and inside it, you have individual cells. These cells are small views that are added as subviews to your scrollview at specific offsets. You then add your images to these cells.
When a user scrolls the scrollview, you first ask the scrollview to dequeue a new cell for you, much the same way you'd ask a table view to dequeue a cell for you to use. This saves the cost of an allocation, if one has been recycled. If you cannot dequeue one from a recycled set, then what you have to do is quite simple: allocate one as you are doing currently.
Furthermore, to implement this cell recycling, what you need to do is see which cells are visible on screen. If one or more cells go off screen, you add them to the recycled cells NSSet which you create. This set just holds cells for later recycling. There's some sample code Apple has which demonstrates this, and it's called PhotoScroller. It also is demonstrated in a WWDC10 video, session 104. I suggest you watch it. Ignore the parts about tiling, you don't need to know that for your purpose.
Once you have this in place, this will force you to set up your cells only when they're needed, which is also another key aspect of this behaviour.
Finally, when you do receive a memory warning, just drop the recycled cells set. If you ever get high enough that this matters, that is, you'll save a few megs of memory. :) (Do not forget to implement it though, ever, when you hold onto temporary data that you don't specifically need around...cells which are not visible on the screen are a good example of this, as are caches.)

You should lazy load your images and only load the images you need at that time. Your app can't show all those images on one screen anyways, so on your scrollview you should only load those images that can fit on the screen and maybe a few around that, and as the user scrolls to release the images it no longer needs.

Related

IOS: Memory Issue on living objects and

I have an image gallery which is developed using UICollectionView. After I select multiple images from the gallery and press 'Done'. Selected images will display in a UIScrollView (paging on) and those are full screen images that can be scrolled up/down to see other images.
1) Is it ok to have this amount of living objects and heap allocation, when starting the app.'
2) When I select and scroll through the images of UIScrollView allocated memory for images will added to the memory and not release. It will crash the app.
Here are the screens when I start scrolling up/down through the images. It will Increasing the memory consumption also other processes.
This is testing on iPhone5, using Activity monitor VM and Real memory going up.
Why don't you try to load the images 3 at a time, and not the whole set of images at once.
I mean you can set your Scroll Views array and add as many [NSNull null] objects to it as the number of images to be shown. Then, at a particular instance, have only 3 images in that array and after scrolling remove the unnecessary ones or replace it with [NSNull null] objects.
Profiling memory is tricky. The best instrument to actually use is the Activity Monitor, which will tell you how much live memory is really being used, unlike Allocations which will tell you more than you need to know.
Also, the maximum amount of memory is dependent on each device. If you really want to know if you're using a lot of memory, just test it on a device. You generally have a lot of flexibility, and the internal UIImage class does a lot of fancy caching and clearing of said caching for you.

Memory Leaks vs High Memory Allocations?

In my iPhone app I have a very long table which I am loading from xml (online xml). This table shows one image and some text data in a row. I am also using Lazy Loading to load image.
Now in instruments there is no serious leaks, but overall memory allocation raising as I scrolling the table to down (means loading new images).
I want to know will this cause app crashing? My client is saying that the app is crashing on 3GS and iPhone 4, I am testing app on iPod 4G. I am not seeing any crashes but I can see high memory allocations in Instruments.
Please Help!
update -
Yes I am using resuable Cells. When the image loads I add them into the cells using this code -
- (void)appImageDidLoad:(NSIndexPath *)indexPath
{
IconDownloader *iconDownloader = [imageDownloadsInProgress objectForKey:indexPath];
if (iconDownloader != nil)
{
UITableViewCell *cell = (UITableViewCell *)[tblView cellForRowAtIndexPath:iconDownloader.indexPathInTableView];
// Display the newly loaded image
UIImageView *mixtapeImage = (UIImageView *) [cell viewWithTag:TAG_IMAGE];
mixtapeImage.image = iconDownloader.appRecord.mixtape_image_obj;
}
}
You are right that i am not releasing the image because if I scroll up the table I have to add images in cells again thats why I am caching them in "imageDownloadsInProgress" dictionary.
Is there more efficient way of doing this?
The issue could very well be the images being cached, in which case the best solution would be to remove some images from the cache when you get a low memory warning. You could possibly write them to disk temporarily, which would provide a faster load time next time they're needed than re-downloading.
Do you respond to memory warnings? If you do, and you do it properly, your app will not crash.
However, if you are simply scrolling a table view, and memory is building up (does it keep building up, and by how much?) there is something wrong. It could be something as simple as an autorelease pool that is out of place (you are doing image loading), but it could very well be more serious.
Not seeing crashes on one device does not mean it will not crash on devices with a different state (i.e.: low memory state).
You could update your post with a screenshot of instruments, maybe the object allocations list, etc.
Memory increasing as you scroll is not a good sign. A common issue is that you are reusing cells and you keep piling elements on top of each other. Cells look fine because you only see the last layer of UI elements. These are not leaks because they are referenced by the table cell as parent view.
In terms of diagnostics, try the "Allocations" instruments to see if you have more live objets of some type (say, UIImages) than you expect. This is how you would do it:
In the Allocations instrument, using
the top-right search box, search for
"Image". Then look for the column
labeled #linving in UIImage. That
number is the number of UIImage
objects. That is a better compass in
to figuring out where are your
UIImages going. If at some point you
have more objects than what you
expect, and thease UImages are not
going away you have a UIImage leak in
your hands. An UIImage leak will kill
your app real fast.
quote from this answer.
Yes, it can cause a crash. Once you've taken into account the background processes, the shared libraries and your own code, your app only has a few megabytes of memory available on some iOS devices for custom data. If you blow through that, even if you can account for all of the allocations, then you will get killed by the low memory system. Or your app will, anyway.
You've already launched Instruments, which puts you in the top 1% of iOS app developers for knowing how your app behaves on the device. Well done! The next step is to understand why you're monotonically increasing memory usage as the table scrolls through. OK, so you lazily load images when they're needed, but do you dispose of them again when they're not? That should be easy to do using a UITableView with reusable cells, but perhaps you're caching the images somewhere and never emptying the cache. What happens in Instruments when you send a low memory warning in the simulator?
What does your cellForRowAtIndexPath method look like? Do you actually autorelease the cell you allocate in there?
There's no need for you to retain images - iOS is very good at caching results, and if it wants to release cells for memory management reasons, you should let it.
iOS versions pre-4.0 have lower limits for memory allocation. That's why the app crashes on your client's iPhone but not on yours.

Seeing malloc allocating large chunks of memory - trying to track down why (iPhone)

I'm seeing my app being killed by iOS with an out of memory message, however, while tracing the progress of the app in the Allocations Instrument, I see lots of mallocs that seem to be occurring outside of the code I've written.
I'm not seeing any leaks being caught, so I assume these allocations are supposed to be there. Thing is, because I'm not sure about why they have been allocated, I'm not sure what I can do to optimize the app and prevent the OS from jettisoning my app.
Does anyone know why the memory is being allocated, or is there any way for me to find out?
Here are a couple of shots from Instruments showing the mallocs. In the second shot, all of the allocations have the same stack trace.
EDIT
I' displaying a single large image as the UIView background (1024x768), then overlaying a smaller (600px square) UIView with some custom drawing and a third UIView (550px square) over the top of those that contains two 550px square images overlayed.
I'm guessing that this is not appropriate, and there is probably a better way of achieving the composition of views I need for the app to work.
Should this be possible on the iPad?
I think there's not really much information to go on here - if you add a bit more information about what this view in your app is doing you might get some more informed suggestions.
From the screenshot, it would appears large blocks are being allocated to display an image.
Given that I'd hazard a guess that either you're trying to display some very large images, or you UIView is large, or you have more UIViews in memory that you need to display the current screen.
I guess the easiest way to track down exactly where they're coming from would be to disable the part of the application you suspect then run again and see if the allocations still occur.
EDIT
Are all the images the same size as you're displaying them? (ie. are you trying to display a 5M photo as the 1024x768 background?) If not you probably need to scale them down to the size you are display them, or at least closer.
If you're not needing transparency, make sure to make all the views opaque.
I figured out the source of the problem - I was using
[UIImage imageNamed:#'Someimage']
to load in my images. This, as I'm sure many people are aware, caches the image data. I had enough images of sufficient size to cause my app to be jettisoned.
The problem was apparent not because of the size of the image but because of both the size and number of images I was using. The lesson here is be careful with [UIImage imageNamed:].
Thanks for all of the help, chaps!
Mallocs can occur inside of other API's that your app calls (such as loading images, views, playing long sounds, etc.) You can try changing the size of your images, views, sounds and other objects by various amounts as a test, and see if the size of the malloc'd memory changes track one of the changes that you've made.

iPhone: Reading many images quickly

I've got an app I'm working on where we handle a LOT of images at once in a scrollview. (Here's how it looks, each blue block being in image on a scrollview expanding to the right: http://i.stack.imgur.com/o7lFx.png) So to be able to handle the large strain doing this puts on memory. So I've implemented a bunch of techniques such as reusing imageviews etc which have all worked quite successfully in keeping my memory usage down. Another thing I do is instead of keeping the actual image in memory (which I of course couldn't do for all of them because that would run out of memory very quickly) I only keep the image's filepath in memory and then read the image when the user scrolls to an area of the scroll view near that image. However, although this is memory efficient, it's causing a LOT of lag in the scrollview because of the fact that it has to constantly read images from the disk. I can't think of a good solution on how to fix this. Basically right now the app draws to the screen only the visible uiimageviews and while the user scrolls the app will look to see if it can dequeue another imageview so it doesn't have to allocate another one and at that point it reads the image into memory, but as I said it's causing the scrolling action to be very slow. Any ideas on a strategy to use to fix this? Does anyone know what the native photos app does to handle this kind of thing? Thanks so much!
I can suggest you a simple solution to balance both the memory and the computer processing. You only keep small images like thumbnails in memory and only keep about 20 of them. One project that I am doing, I keep 20 thumbnail images (100 x 100) recently accessed, which doesn't cost a lot of memory. I believe that it costs about 200 kb all the time but comparing to a general available memory. I think it is good enough.
It also depends on your use case : if user scroll really fast and you don't know when will they go. You can have even smaller images than the thumnail and when you show it on the UIImageView, you resize it to fit. When user stops scrolling for a while. You can start loading bigger images and then you have a nicer images. User may not even notice about the process
I don't think there is a solution that can be fast and using as less memory as possible. Because we have memory, maybe not big but have enough if we use it smartly.
Slow scrolling performance might mean that you're blocking the main thread while loading images. In that case, the scrolling animation won't continue until the images are loaded, which would indeed cause pretty choppy scrolling performance.
It would be better to lazily load requested images in the background, while the main thread continues to handle the scrolling animation. A library that provides this functionality (among other things) is the 'three20' library. See the Tidbits document, and scroll down to the bottom where the 'TTImageView' class is described.
I had a similar issue with a PDF viewer, The recommended way to do this is to have as low a res image as you can get away with and if you are allowing the user to blow the image up/zoom, then have two versions or three versions of that image increasing the res as you go.
Put as much code as you can get away with in the didDecelerate method (like loading in higher res images like vodkhang talks about), rather than processing loads in didScroll. Recycle Views out of scope as you have said. and beware of autoreleased Context based Image Creation functions.
Load images in on background threads intelligently (based on the scrollView Offset position and zoom level), and think about using CALayer/Tiled Layer drawing for larger images.
Three20 (an open source iOs lib) has a great Photo Viewer that can be subclassed, it has thumbnail navigation, large image paging, caching and gestures right out of the box.

App like default photo browser in iphone?

i am developing a app which contains feature like default photo browser in iphone. I done some what similar to that. but after loading some(near about 10-15) images from remote server,i am receiving memory warning.My requirement is loading image one by one. For this, on scroll view i am putting an images and increasing the contentSize of scroll view. it will work fine. but due to memory warning app quite.
Guys, any have any idea to approach for this feature which work similar to photo app without problem?
thanks in advance .
You're running out of memory because you're keeping the data for 10 or more images in memory at one time. You need to have more logic in your code that not only preloads and increases the scroll view's content size, but also removes UIImageViews from the scrollview (and thus from memory) as the user scrolls to newer stuff. (You can also save "evicted" images to the cache area on disk so if the users scrolls back you don't have to go to the server again.)
If you use a UITableView, it will request the images only when needed, and will automatically purge off-screen cells to save memory. It may not fit into the aesthetic for your application, though.