У меня есть UIView, который содержит около 80 UIImageViews. Когда я создаю новый набор из 80 UIImageViews каждый раз, когда UIView загружается с новыми изображениями, память из сброшенных UIViews не восстанавливается. (Решение состоит в том, чтобы продолжать перерабатывать один и тот же набор UIImageViews, но меня беспокоит то, что память не восстанавливается, потому что в конечном итоге я хочу получить память даже из переработанных UIImageViews обратно.)
Вот мой пример кода:
#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
Два важных метода: showWithNewImages и showWithRecycledImages. Первый выделяет новые UIImageViews каждый раз, когда он вызывается, а второй просто повторяет тот же набор, если UIImageViews.
Вот вывод:
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
Прежде чем продолжить, я должен отметить, что я наблюдал за отличной сессией 104 «Разработка приложений с представлениями прокрутки» из WWDC 2010, и я знаю все о том, как и зачем перерабатывать UIImageViews.
Тем не менее, сессия предполагает, что цель рециркуляции состоит в том, чтобы избежать выделения UIImageView для каждого возможного изображения, поскольку когда-либо видны только два, а третий нужен для анимации. Чего они не упоминают, так это того, что память, используемая этими тремя UIImageViews, вообще исчезает при выгрузке всего представления.
Хорошо, теперь вопрос: есть ли способ вернуть память из подпредставлений UIImageView, если удаление их из суперпредставления не делает этого?
(Это не проблема кэширования самих UIImages, поскольку оба примера используют один и тот же набор UIImages.)
Идеи кому-нибудь?
Примечание. Меня также беспокоит увеличение памяти даже при утилизации. Это в конечном итоге убьет мое приложение, так как я показываю слайд-шоу с тысячами изображений, которые будут работать часами.
вклад Тии (см. Ниже) не является ответом. (Как примечание, я не изменял массив; я изменял состояние, для которого массив является моментальным снимком.) Чтобы доказать это, я переписал цикл удаления-удаления вида так:
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];
с точно такими же результатами. (Объем усиления памяти идентичен.)