Note that there are some explanatory texts on larger screens.

plurals
  1. POUIImageView memory not being recovered (long and detailed)
    primarykey
    data
    text
    <p>I have a UIView that contains about 80 UIImageViews. When I create a new set of 80 UIImageViews each time the UIView is loaded with new images, memory from the discarded UIViews is not being recovered. (The solution is to keep recycling the same set of UIImageViews, but my concern is that the memory is not being recovered, because eventually I do want the memory even from the recycled UIImageViews back.)</p> <p>Here's my example code:</p> <pre><code>#import "ImageMemViewController.h" #import &lt;mach/mach.h&gt; static NSArray *paths; static NSMutableArray *images; static NSMutableArray *imageViews; static vm_size_t report_memory(void) { struct task_basic_info info; mach_msg_type_number_t size = sizeof(info); kern_return_t kerr = task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&amp;info, &amp;size); if (kerr == KERN_SUCCESS) { NSLog(@"Memory used: %f MB", (float)info.resident_size / (1024 * 1024)); return info.resident_size; } else NSLog(@"Error: %s", mach_error_string(kerr)); return 0; } @implementation ImageMemViewController - (void)viewDidLoad { [super viewDidLoad]; paths = [NSArray arrayWithObjects: @"MJR_20070519_0529.jpg", @"MJR_20070519_0537.jpg", @"MJR_20070519_0545.jpg", @"MJR_20070519_0559.jpg", nil]; images = [[NSMutableArray alloc] initWithCapacity:paths.count]; for (NSString *p in paths) [images addObject:[UIImage imageNamed:p]]; imageViews = [[NSMutableArray alloc] initWithCapacity:100]; for (int i = 0; i &lt; 100; i++) [imageViews addObject:[[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 0, 0)]]; } - (void)showWithNewImages { // Question: What can be done here to reclaim memory? #if 0 // this was tried for (UIImageView *v in self.view.subviews) { v.image = nil; [v removeFromSuperview]; } #else // as was this -- no difference NSArray *subviews = self.view.subviews; for (int i = 0; i &lt; subviews.count; i++) { UIImageView *v = [subviews objectAtIndex:i]; v.image = nil; [v removeFromSuperview]; } #endif int n = 0; for (int x = 10; x &lt; 700; x += 100) for (int y = 10; y &lt; 1000; y += 100) { UIImageView *v = [[UIImageView alloc] initWithFrame:CGRectMake(x, y, 90, 90)]; v.image = [images objectAtIndex:n % paths.count]; n++; [self.view addSubview:v]; [v release]; } } - (void)showWithRecycledImages { for (UIImageView *v in self.view.subviews) { v.image = nil; [v removeFromSuperview]; } int n = 0; for (int x = 10; x &lt; 700; x += 100) for (int y = 10; y &lt; 1000; y += 100) { UIImageView *v = [imageViews objectAtIndex:n]; v.frame = CGRectMake(x, y, 90, 90); v.image = [images objectAtIndex:n++ % paths.count]; [self.view addSubview:v]; } } - (void)viewDidAppear:(BOOL)animated { vm_size_t start = report_memory(); for (int i = 0; i &lt; 1000; i++) { [self showWithRecycledImages]; if (i % 250 == 0) report_memory(); } NSLog(@"showWithRecycledImages memory gain = %f MB", (float)(report_memory() - start) / (1024 * 1024)); start = report_memory(); for (int i = 0; i &lt; 1000; i++) { [self showWithNewImages]; if (i % 250 == 0) report_memory(); } NSLog(@"showWithNewImages memory gain = %f MB", (float)(report_memory() - start) / (1024 * 1024)); } @end </code></pre> <p>The two important methods are showWithNewImages and showWithRecycledImages. The former allocates new UIImageViews each time it's called, and the latter just recycles the same set if UIImageViews.</p> <p>Here's the output:</p> <pre><code>2010-11-06 10:51:28.556 ImageMem[7285:207] Memory used: 12.496094 MB 2010-11-06 10:51:28.582 ImageMem[7285:207] Memory used: 12.636719 MB 2010-11-06 10:51:45.358 ImageMem[7285:207] Memory used: 12.898438 MB 2010-11-06 10:52:03.409 ImageMem[7285:207] Memory used: 13.218750 MB 2010-11-06 10:52:17.430 ImageMem[7285:207] Memory used: 13.546875 MB 2010-11-06 10:52:28.071 ImageMem[7285:207] Memory used: 13.863281 MB 2010-11-06 10:52:28.084 ImageMem[7285:207] showWithRecycledImages memory gain = 1.367188 MB 2010-11-06 10:52:28.087 ImageMem[7285:207] Memory used: 13.863281 MB 2010-11-06 10:52:28.153 ImageMem[7285:207] Memory used: 13.871094 MB 2010-11-06 10:52:40.717 ImageMem[7285:207] Memory used: 17.746094 MB 2010-11-06 10:52:46.620 ImageMem[7285:207] Memory used: 21.621094 MB 2010-11-06 10:52:49.684 ImageMem[7285:207] Memory used: 25.464844 MB 2010-11-06 10:52:52.772 ImageMem[7285:207] Memory used: 29.347656 MB 2010-11-06 10:52:52.775 ImageMem[7285:207] showWithNewImages memory gain = 15.484375 MB </code></pre> <p>Before continuing, I should note that I have watched the excellent Session 104, "Designing Apps with Scroll Views" from WWDC 2010, I and know all about how and why to recycle UIImageViews.</p> <p>However, the session suggests that the purpose of recycling is to avoid allocating a UIImageView for every possible image, since only two are ever seen and a third one is needed for animation. What they don't mention is whether the memory used by those three UIImageViews even goes away when the entire view is unloaded.</p> <p>OK, now for the question: Is there any way to get the memory back from the UIImageView subviews if removing them from their superview doesn't do it?</p> <p>(This is not an issue of caching of the UIImages themselves, as both examples use the same set of UIImages.)</p> <p>Ideas anyone?</p> <p>Note: I am also concerned with the memory gain even when recycling. This will eventually kill my app, since I am showing a slide show of thousands of images that will run for hours.</p> <hr> <p>tia's contribution (see below) is not the answer. (As a side note, I was not modifying the array; I was modifying the state of which the array is a snapshot.) To prove this, I rewrote the subview-removal loop to be this:</p> <pre><code>UIImageView *views[200]; // Note: C array; does not affect retain counts. int j = 0; for (UIImageView *v in self.view.subviews) views[j++] = v; for (int i = 0; i &lt; j; i++) [views[i] removeFromSuperview]; </code></pre> <p>with exactly the same results. (Memory gain amount is identical.)</p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload