收到内存警告后崩溃

7

我希望在图片数量超过70张时,能够在滚动视图中显示多个图片。但是,应用程序会崩溃并显示内存错误。我已经从文档目录中获取了这些图片。

我尝试过 -

 UIScrollView *MyScroll=[[UIScrollView alloc]initWithFrame:CGRectMake(0, 0, 320, 480)];

    int x=5,y=15;

    for (int i = 0; i < imgarr.count; i++)
    {
        if(x<=211)
        {

        UIImage* imagePath = [UIImage imageWithContentsOfFile:[path_array objectAtIndex:i]];

        imgView=[[UIImageView alloc]initWithFrame:CGRectMake(x, y, 77, 75)];
        imgView.image=imagePath;

            imgeBtn=[UIButton buttonWithType:UIButtonTypeCustom];
            imgeBtn.frame=CGRectMake(x, y, 93, 110);
            [imgeBtn addTarget:self action:@selector(btnclick:) forControlEvents:UIControlEventTouchUpInside];
            imgeBtn.tag=i;
            x+=103;

        }
        else
        {
            x=5;
            y+=130;


        UIImage* imagePath = [UIImage imageWithContentsOfFile:[path_array objectAtIndex:i]];

        imgView=[[UIImageView alloc]initWithFrame:CGRectMake(x, y, 77, 75)];
        imgView.image=imagePath;

            imgeBtn=[UIButton buttonWithType:UIButtonTypeCustom];
            imgeBtn.frame=CGRectMake(x, y, 93, 110);
            [imgeBtn addTarget:self action:@selector(btnclick:) forControlEvents:UIControlEventTouchUpInside];
            imgeBtn.tag=i;
            x+=103;

        }
        [MyScroll addSubview:imgeBtn];
        [MyScroll addSubview:imgView];
         MyScroll.contentSize=CGSizeMake(320, y+120);

我该如何在滚动视图中显示多张图片?

请问您为什么要使用 if(x<=211)?您是想将所有图像显示为水平还是垂直排列? - Santu C
2
ScrollView 不是展示多张图片的更好方法。UICollectionView 是更好的展示图片的方法,因为它是可重用的。在 ScrollView 中,每次分配内存,应用程序会崩溃或加载时间过长。 - Dharmbir Singh
我已经尝试了UIScrollView和UICollectionView,但结果都一样。 - sohil
最好使用UICollectionView代替UIScrollView,因为在UICollectionView中我们能够创建可重用的单元格。 - A_Curious_developer
@sohil,请更好地使用您的赏金,您现在正在消耗宽限期。 - Syed Ali Salman
显示剩余3条评论
7个回答

10
其他人建议你切换到 UICollectionView 是好的,但是他们忽略了您代码中真正的缺陷: UIImage 的方法imageNamed: 永远在内存中保存您的 70 张图像。 根据文档:
  

如果您有一个仅需要显示一次的图像文件,并希望确保它不会被添加到系统缓存中,您应该使用 imageWithContentsOfFile: 创建您的图像。 这将使您的单次使用的图像不会被放入系统图像缓存中,可能改善应用程序的内存使用特性。

使用此方法将防止崩溃,如果只在需要时加载图像到内存中。在UICollectionView中,这很容易实现,因为您只需在集合视图要求您在数据源的 collectionView:cellForItemAtIndexPath: 方法中填充单元格对象时加载图像即可。

5
Create custom class of collection view cell 
and You can pass image name in array. 

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController<UICollectionViewDataSource,UICollectionViewDelegate>

@property (weak, nonatomic) IBOutlet UICollectionView *Collection;

@end

#import "ViewController.h"
#import "CustomCell.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad
{
    [super viewDidLoad];
    [self.Collection registerClass:[CustomCell class] forCellWithReuseIdentifier:@"cell"];
    [self.Collection registerNib:[UINib nibWithNibName:@"CustomCell" bundle:nil] forCellWithReuseIdentifier:@"cell"];
}

-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
    return 5;
}
-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
    return 4;
}

-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    CustomCell * cell=[collectionView dequeueReusableCellWithReuseIdentifier:@"cell" forIndexPath:indexPath];
    cell.imgview.image=[UIImage imageNamed:@"XYZ.png"];
    return cell;
}

2
也许NSCache能帮助您。
1. 当您在第10个索引处显示图像时,您可以将图像5-9和11-16缓存到NSCache中。 2. 当scrollView开始滚动时,从NSCache中获取所需的图像,并删除您不需要显示的对象。 3. 我认为这样可以提高效率。苹果提供了一个演示如何使用缓存和UICollectionView处理大量资源的示例。您可以看一下。 https://developer.apple.com/library/ios/samplecode/UsingPhotosFramework/Introduction/Intro.html

2
您正在分配并不断添加图像,这会导致内存崩溃,因为所有图像都占用设备内存。有许多解决方案可用于显示图像,最好的方法是使用UICollectionView,它仅加载显示在屏幕上的图像。您可以查找一些好的教程来学习如何做到这一点,这里提供一个参考链接

抱歉,它无法工作。我已经使用了UIColllectionView、UITabelview和Scollview,但结果都是一样的。我从文档目录中获取图像。 - sohil

1
我也曾经遇到过同样的问题,我使用以下简单的方法解决了它。
使用以下代码代替原有代码:
[UIImage imagedNamed:@"yourImage"];

try using this:

[yourCell.imageview setImage:[UIImage imageWithContentsOfFile:[[NSBundle mainBundle]pathForResource:@"yourImage" ofType:@"png"]]];

我希望通过使用imageWithContentsOfFile来释放内存,从而解决您的问题。这样,您就不会收到Memory Warning的提示。 更新: 您还可以使用UIImage Class的其他方法来显示来自任何远程URL和应用程序文档目录的图像。
如果有帮助,请告诉我。

UIImage imageWithContentsOfFile:]确实会消耗内存,只是这不是永久性的。此外,仅仅因为您可以使用此方法从任何URL获取图像并不意味着您应该这样做;此方法是同步的,在这种情况下永远不应从主线程调用。 - Ben Pious
是的,这就是问卷出现问题的原因。这个内存将被释放。其次,使用URL取决于问卷逻辑...这只是一个选项,伙计。还有一件事,你刚才是不是给我点了踩并发表了评论来证明你的踩? - Syed Ali Salman
你说“不消耗内存”,而不是“在释放后不会停留在内存中”。这有很大的区别。至于“远程URL”这件事,只有在您不关心软件质量的情况下才是一个选项。苹果关于类似方法([NSData dataWithContentsOfURL:])的文档明确表示不要这样做。 - Ben Pious
你能否编辑我的回答而不是给它点踩呢? - Syed Ali Salman

0

使用@autoreleasepool。以下是有关@autoreleasepool的详细信息

@autoreleasepool语句执行的工作与以前相同,只是不再使用NSAutoreleasePool类。NSAutoreleasePool的工作方式有点奇怪,因为创建它会在整个应用程序中产生影响;@autoreleasepool创建了一个作用域区域,并清楚地指出了池内的内容以及何时排空(当它超出作用域时)。根据苹果公司的说法,这也更有效率。

autorelease池的概念很简单,每当一个对象实例被标记为自动释放(例如NSString *str = [[[NSString alloc] initWithString:@"hello"] autorelease];),它在那个时刻的保留计数为+1,但在运行循环结束时,池被排空,任何标记为自动释放的对象都将其保留计数减少。这是一种在您准备好让对象保留自己时保留对象的方法。

使用ARC时,虽然开发人员不使用autorelease关键字,但管理ARC的底层系统会为您插入该关键字。(请记住:所有ARC所做的就是在适当的时间为您插入保留、释放和自动释放调用)。因此,现有的AutoreleasePool概念需要保留。

如果您移除自动释放池,则对象将开始泄漏。 欲了解更多详情请点这里


0
你可以尝试使用UITableView。假设你有n张图片,那么 -
 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
        return 1;
    }
        - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
        {
            // 3 is numbers of images per row
            if (n/3 *3 == n)
                return  n/3;

            return n/3 +1;
        }

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    static NSString *CellIdentifier = @"Cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    if (cell == nil) {

        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] ;

    }


    int x=5,y=15;

   UIImageView* imgView1=[[UIImageView alloc]initWithFrame:CGRectMake(x, y, 93, 110)];
   imgView1.image=[UIImage imageNamed:[imgarr objectAtIndex:indexPath.row + 0]];
    x+=103;

   UIImageView* imgView2=[[UIImageView alloc]initWithFrame:CGRectMake(x, y, 93, 110)];
    imgView2.image=[UIImage imageNamed:[imgarr objectAtIndex:indexPath.row + 1]];

    x+=103;

    UIImageView* imgView2=[[UIImageView alloc]initWithFrame:CGRectMake(x, y, 93, 110)];
    imgView2.image=[UIImage imageNamed:[imgarr objectAtIndex:indexPath.row + 2]];


    [cell.contentView addSubview:imgView1];
     [cell.contentView addSubview:imgView2];
     [cell.contentView addSubview:imgView3];
    return cell;

}

你试过了吗?看一下"[imgarr objectAtIndex:indexPath.row + 0]"返回的是什么。我觉得它显示了重复的图片。 - V.J.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接