滚动UIScrollView时内存泄漏

时间:2012-06-15 10:19:12

标签: iphone memory-management uiscrollview

在我的iPhone应用中,我正在动态添加UIScrollView并在滚动视图中添加n个UIImageUIButton s。这里,图像从不同的URL加载,按钮标题来自SQlite数据库。一切都好。但是当我滚动滚动视图时,现在我得到内存警告级别= 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];
}

7 个答案:

答案 0 :(得分:4)

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

  2. 当滚动视图滚动时,获取滚动视图的可见矩形,并添加适合范围或超过该矩形的图像和按钮。

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

答案 1 :(得分:2)

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

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

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

第三个选项是查看您的代码并尝试找出自己发生的事情。了解内存管理是ios设备编程的重要组成部分。

如何在滚动视图中发布您正在使用的代码,我们可以看一下吗?

答案 2 :(得分:2)

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

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

如果在滚动视图仍处于运行状态时收到内存警告,则问题可能不是泄漏,而是内存过度使用。如果您继续将图像添加到大型滚动视图中,您肯定会遇到内存过剩问题。

要使用图像填充大型滚动视图,但避免记忆过剩,您可以查看ScrollViewSuite演示的第三个示例,即平铺。这应该适合你,因为你的图像和按钮大小相同,可以充当瓷砖。

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

从滚动视图的大小来看,除了平铺和回收之外,您可能没有其他选择。尽管如此,这是一个不错的选择。

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

答案 3 :(得分: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];
}

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

这就是它。表视图一如既往地负责回收您的单元并优化内存使用。

答案 4 :(得分: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];
}

可能对你有所帮助

答案 5 :(得分:1)

这里有3条建议:

  • 尝试在后台线程中加载图片
  • 检查此回复Does iOS 5 have garbage collection?
  • 使用leakinstrument找出您的应用程序泄漏的位置,然后管理该部分以获得最佳效果

答案 6 :(得分:1)

这是Apple的错误。 UIScrollView甚至会泄漏这些代码:

UIScrollView *s = [[UIScrollView alloc] initWithFrame:self.view.bounds];
s.contentSize = CGSizeMake(320, 800);
[self.view addSubview:s];
[s release];