我有一个包含大约80个UIImageViews的UIView。每当UIView加载新图像时,我创建一组新的80个UIImageViews时,丢弃的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
在继续之前,我应该注意到我已经看过WWDC 2010中的优秀会议104,“使用滚动视图设计应用程序”,我知道如何以及为什么要回收UIImageViews。
然而,会议建议回收的目的是避免为每个可能的图像分配UIImageView,因为只有两个被看到,第三个是动画需要的。他们没有提到的是,当整个视图被卸载时,这三个UIImageViews使用的内存是否会消失。
好的,现在提出问题:如果从UIImageView子视图中删除内存,是否有任何方法可以从超级视图中删除内存?
(这不是UIImages本身的缓存问题,因为这两个例子都使用相同的UIImages集。)
想点什么?
注意:即使在回收时,我也关注内存增益。这最终会杀死我的应用程序,因为我正在显示数千张图片的幻灯片放映,这些图片将运行数小时。
tia的贡献(见下文)不是答案。 (作为旁注,我没有修改数组;我正在修改数组快照的状态。)为了证明这一点,我重写了子视图删除循环:
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];
具有完全相同的结果。 (记忆增益量相同。)
答案 0 :(得分:1)
我怀疑这里有两点:
[self.view.subviews copy]
。removeFromSuperview
可能会autorelease
而不是release
,因此在这种情况下不会立即回收内存。编辑:
这里有一个内存泄漏:
[imageViews addObject:[[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 0, 0)]];
也许您忘了发布图片视图。
还有一点是,如果要恢复内存,还需要释放或删除imageViews
中的所有项目。