0

在我的 iPhone 应用程序中,我动态添加 aUIScrollView并将 n 个UIImages 和UIButtons 添加到滚动视图中。在这里,图像从不同的 url 加载,按钮标题来自 SQlite 数据库。一切安好。但是当我滚动滚动视图时,现在我收到内存警告 Level=1 并且过了一段时间它是 Level=2 并导致应用程序崩溃。我正在使用 ARC。我该如何解决这个问题?

代码

- (void)setUpViewLayout{
    int newContentSize = [appDelegate.itemArray count] * 125;

    menuItemIdArray = [[NSMutableArray alloc]init];
    mainView = [[UIScrollView alloc]initWithFrame:CGRectMake(0, 100, 480, 220)];
    mainView.contentSize = CGSizeMake(newContentSize, 220);
    mainView.tag = 100;
    mainView.delegate = self;
    mainView.userInteractionEnabled = YES;
    mainView.backgroundColor = [UIColor clearColor];

    int xPosition = 20;

    for (tagVal = 0; tagVal < [appDelegate.itemArray count]; tagVal++) {
        [self createImage:xPosition];
        [self createButton];
        xPosition = xPosition + 120;
    }
    [self.view addSubview:mainView];
}

- (void)createImage:(int)xPosition{
    DataBaseClass *itemObj = [appDelegate.itemArray objectAtIndex:tagVal];

    NSString *url = [NSString stringWithFormat:@"%@",itemObj.notAvialableIcon];
    imgView = [[UIImageView alloc]initWithFrame:CGRectMake(xPosition+8, 48, 110, 123)];
    imgView.userInteractionEnabled = YES;
    imgView.tag = tagVal;

    [imgView setImageWithURL:[NSURL URLWithString:url] placeholderImage:[UIImage imageNamed:@"item01.png"]];

    [mainView addSubview:imgView];
}
- (void)createButton{
    DataBaseClass *itemObj = [appDelegate.itemArray objectAtIndex:tagVal];
    button = [UIButton buttonWithType:UIButtonTypeCustom];
    button.frame = CGRectMake(5, 90, 100, 26);
    button.tag = tagVal;
    button.userInteractionEnabled = YES;
    button.tag = tagVal;
    button.titleLabel.textColor = [UIColor blueColor];
    button.titleLabel.font = [UIFont systemFontOfSize:9.0];
    NSString *name = [NSString stringWithFormat:@"%@",itemObj.itemStatus];
    itmName = [NSString stringWithFormat:@"%@",itemObj.itemName];

    NSString *date = [self changeDateFormat:itemObj.itemReleaseDate];          
    [button setTitle:date forState:UIControlStateNormal]; 
    button.userInteractionEnabled = NO;
    button setBackgroundImage:[UIImage imageNamed:@"not_available_bttn_bck_img"] forState:UIControlStateNormal];

    [imgView addSubview:button];
}
4

7 回答 7

4
  1. 不要一次将所有子视图添加到滚动视图。那太贵了。

  2. 当滚动视图确实滚动时,获取滚动视图的可见矩形,并添加您的图像和按钮,使其刚好适合范围或超过该矩形的一小部分。

  3. 当可见子视图不可见时,从超级视图中移除。

于 2012-06-15T10:47:41.053 回答
2

您需要确定代码的哪一部分导致泄漏。您可以通过多种方式做到这一点。

一种方法是使用 xcode 中的内置分析器。它分析您的代码并检测(一些)潜在的内存泄漏。

仪器工具也是发现这些泄漏的好工具。使用分配/泄漏组件启动它。转到您的滚动视图,并在滚动视图后执行示例。你的泄漏应该出现。现在您可以追踪泄漏并让仪器直接在您的代码中找到正确的位置。

第三种选择是检查您的代码并尝试自己弄清楚发生了什么。了解内存管理是 ios 设备编程的重要组成部分。

在此处发布您在滚动视图中使用的代码怎么样,以便我们看一下?

于 2012-06-15T10:29:11.260 回答
2

Abhishek 是绝对正确的,所有子视图都必须在添加到父视图后释放。这将导致泄漏。具体来说,一旦滚动视图离开屏幕并被释放,它的子视图将不会被释放。从分配时起,它们的保留计数仍为 1。

但是,只要滚动视图仍在屏幕上,就没有泄漏。超级视图保留其所有子视图(即,将其保留计数增加 1。)如果子视图已分配但未释放,则保留计数为 2。如果已分配并释放,其保留计数为 1。无论哪种方式,只要滚动视图存在,它的子视图仍然正确地保留。

如果您在滚动视图仍处于打​​开状态时收到内存警告,则问题可能不是泄漏,只是过度使用内存。如果你不断地在一个大的滚动视图中添加图像,你肯定会遇到内存溢出的问题。

要使用图像填充大型滚动视图,但避免内存溢出,您可以查看ScrollViewSuite演示的第三个示例,关于平铺。这应该很适合您,因为您的图像和按钮大小相同,并且可以充当图块。

这个想法是从滚动视图中创建一种表格视图,现在回收图像块而不是单元格。滚动视图是子类化的,一组可重用的图块作为其实例变量之一保存。实现的关键是,在 layoutSubviews 中,从 superview 中移除已经移出可见区域的图块,然后为新可见的内容回收图块并将它们添加为子视图。这样,只有可见的图块被加载到内存中。而且,它回收瓷砖,就像表格视图回收单元格一样。

从滚动视图的大小来看,您可能别无选择,只能平铺和回收。尽管如此,这是一个不错的选择。

更新: Wubao Li 基本上总结了需要做的事情。ScrollViewSuite 演示向您展示了如何操作。

于 2012-06-15T11:38:45.030 回答
2

我正在为此打开一个新的回复。这是我们都错过的简单解决方案。

从您发布的代码来看,这只是一个表格视图。因此,您不必构建自己的平铺滚动视图。

这里有一些代码可以帮助您入门。设置表格视图时,将其旋转 90 度,设置行高并消除分隔线:

tableView.transform = CGAffineTransformMakeRotation(0.5 * M_PI);
tableView.rowHeight = 120.0;
tableView.separatorStyle = UITableViewCellSeparatorStyleNone;

您必须设置表格视图的框架,使其在旋转后处于正确位置。本质上,它与您当前的滚动视图的框架相同,或者与其侧面的框架相同。

下面是几个表视图的数据源方法:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return [appDelegate.itemArray count];
}

表格单元格可以是非常简单的自定义表格单元格,只有一个图像视图和该图像视图上的按钮。您还可以旋转图像视图,以便正确显示图像,而不是侧面显示。或者,您可以在加载之前在照片编辑器或图像编辑器中旋转所有图像。

差不多就是这样。与往常一样,表格视图将负责回收您的单元格并优化您的内存使用。

于 2012-06-16T10:15:26.830 回答
1
//you had allocated the things but did not release it ... it was the reason of leak


- (void)setUpViewLayout{
int newContentSize = [appDelegate.itemArray count] * 125;

// menuItemIdArray = [[NSMutableArray alloc]init]; why you are allocating this array

UIScrollView *mainView = [[UIScrollView alloc]initWithFrame:CGRectMake(0, 100, 480, 220)];
mainView.contentSize = CGSizeMake(newContentSize, 220);
mainView.tag = 100;
mainView.delegate = self;
mainView.userInteractionEnabled = YES;
mainView.backgroundColor = [UIColor clearColor];

int xPosition = 20;

for (tagVal = 0; tagVal < [appDelegate.itemArray count]; tagVal++) {
    [self createImage:xPosition];
    [self createButton];
    xPosition = xPosition + 120;
}
[self.view addSubview:mainView];
[mainView relese];//release scroll view here
}

- (void)createImage:(int)xPosition{
DataBaseClass *itemObj = [appDelegate.itemArray objectAtIndex:tagVal];

NSString *url = [NSString stringWithFormat:@"%@",itemObj.notAvialableIcon];
imgView = [[UIImageView alloc]initWithFrame:CGRectMake(xPosition+8, 48, 110, 123)];
imgView.userInteractionEnabled = YES;
imgView.tag = tagVal;

   [imgView setImageWithURL:[NSURL URLWithString:url] placeholderImage:[UIImage  imageNamed:@"item01.png"]];

   [mainView addSubview:imgView];
   [imgView release]; //release imageview here
}
 - (void)createButton{
DataBaseClass *itemObj = [appDelegate.itemArray objectAtIndex:tagVal];
button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = CGRectMake(5, 90, 100, 26);
button.tag = tagVal;
button.userInteractionEnabled = YES;
button.tag = tagVal;
button.titleLabel.textColor = [UIColor blueColor];
button.titleLabel.font = [UIFont systemFontOfSize:9.0];
NSString *name = [NSString stringWithFormat:@"%@",itemObj.itemStatus];
itmName = [NSString stringWithFormat:@"%@",itemObj.itemName];

NSString *date = [self changeDateFormat:itemObj.itemReleaseDate];          
[button setTitle:date forState:UIControlStateNormal]; 
button.userInteractionEnabled = NO;
button setBackgroundImage:[UIImage imageNamed:@"not_available_bttn_bck_img"] forState:UIControlStateNormal];

[imgView addSubview:button];
}

可以帮助你吗

于 2012-06-15T10:35:12.487 回答
1

这里有3个建议给你:

  • 尝试在后台线程中加载图像
  • 检查此响应iOS 5 是否有垃圾收集?
  • 使用leak,instrument找出你的应用程序在哪里泄漏,然后最好地管理该部分
于 2012-06-15T10:48:42.397 回答
1

这是苹果的漏洞。UIScrollView 甚至会泄漏这些代码:

UIScrollView *s = [[UIScrollView alloc] initWithFrame:self.view.bounds];
s.contentSize = CGSizeMake(320, 800);
[self.view addSubview:s];
[s release];
于 2012-07-24T07:38:09.230 回答