J'ai un UIView qui contient environ 80 UIImageViews. Lorsque je crée un nouveau jeu de 80 UIImageViews chaque fois que l'UIView est chargé avec de nouvelles images, la mémoire des UIViews rejetées n'est pas récupérée. (La solution est de continuer à recycler le même ensemble de UIImageViews, mais mon souci est que la mémoire ne soit pas récupérée, car finalement je veux récupérer la mémoire des UIImageViews recyclées.)La mémoire UIImageView n'est pas récupérée (longue et détaillée)
Voici mon exemple de code:
#import "ImageMemViewController.h"
#import <mach/mach.h>
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)&info,
&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 < 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 < subviews.count; i++) {
UIImageView *v = [subviews objectAtIndex:i];
v.image = nil;
[v removeFromSuperview];
}
#endif
int n = 0;
for (int x = 10; x < 700; x += 100)
for (int y = 10; y < 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 < 700; x += 100)
for (int y = 10; y < 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 < 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 < 1000; i++) {
[self showWithNewImages];
if (i % 250 == 0)
report_memory();
}
NSLog(@"showWithNewImages memory gain = %f MB", (float)(report_memory() - start)/(1024 * 1024));
}
@end
Les deux méthodes importantes sont showWithNewImages et showWithRecycledImages. Le premier alloue de nouvelles UIImageViews chaque fois qu'il est appelé, et le dernier recycle simplement le même jeu si UIImageViews.
est ici la sortie:
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
Avant de continuer, je dois souligner que je l'ai vu l'excellente session 104, de la WWDC 2010 « Design Apps avec des vues de défilement », moi et tout savoir sur comment et pourquoi recycler UIImageViews. Cependant, la session suggère que le but du recyclage est d'éviter d'allouer une UIImageView pour chaque image possible, puisque seulement deux sont déjà vues et une troisième est nécessaire pour l'animation. Ce qu'ils ne mentionnent pas, c'est si la mémoire utilisée par ces trois UIImageViews disparaît même lorsque la vue entière est déchargée. OK, maintenant pour la question: Y a-t-il un moyen de récupérer la mémoire des sous-vues UIImageView si le fait de les supprimer de leur aperçu ne le fait pas?
(Ce n'est pas une question de la mise en cache des UIImages eux-mêmes, comme les deux exemples utilisent le même ensemble de UIImages.)
Idées anyone?
Remarque: Je suis également préoccupé par le gain de mémoire, même lors du recyclage. Cela finira par tuer mon application, puisque je montre un diaporama de milliers d'images qui dureront pendant des heures.
La contribution de tia (voir ci-dessous) n'est pas la réponse. (. Comme une note de côté, je ne modifie pas le tableau, je modifier l'état dont le tableau est un instantané) Pour le prouver, je réécris la boucle sous-vue-suppression être ceci:
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 < j; i++)
[views[i] removeFromSuperview];
avec exactement les mêmes résultats. (Quantité de gain de mémoire est identique.)
(La réponse à la réponse de tia apparaît ci-dessus comme une addition à ma question initiale.) –
Cette allocation de UIImageViews n'est pas dans la boucle et ne tient pas compte de l'augmentation de la mémoire. Notez qu'il est commun aux deux alternatives. –
Je vois. Que diriez-vous de drainer autorelease pool après chaque boucle? – tia