收到内存警告然后崩溃
Posted
技术标签:
【中文标题】收到内存警告然后崩溃【英文标题】:received memory warning then crash 【发布时间】:2015-08-08 00:40:45 【问题描述】:我想在滚动视图中显示多个图像,当图像超过 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 Scrollview 并不是显示多个图像的更好方法。 UICollectionView 是显示图像的更好方法,因为它可重用。每次在 Scrollview 中分配内存时,应用程序都会崩溃或负载过大。 我尝试了 UIScrollView 和 UICollectionView 但结果是一样的。 使用 uicollection 视图代替 uiscroll 视图会更好,因为在 uicollection 视图中我们能够创建一个可重用的单元格。 @sohil 请以更好的方式消耗你的赏金,你现在正在消耗宽限期 【参考方案1】:其他人给出的切换到UICollectionView
的建议很好,但它忽略了代码中的真正缺陷:UIImage
的方法imageNamed:
将所有 70 个图像永久保存在内存中。根据文档:
如果您有一个只显示一次的图像文件并希望 确保它不会被添加到系统的缓存中,您应该 而是使用 imageWithContentsOfFile: 创建您的图像。这会 将您的一次性图像保留在系统图像缓存之外,可能 改善应用的内存使用特性。
如果您只将所需的图像加载到内存中,则切换到此方法将防止崩溃。 UICollectionView 使这变得微不足道,因为只有当集合视图要求您在其数据源的 collectionView:cellForItemAtIndexpath:
方法中填写单元格对象时,您才能加载图像。
【讨论】:
【参考方案2】: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;
【讨论】:
【参考方案3】:您正在分配并继续添加导致内存崩溃的图像,因为所有图像都占用了设备内存,有许多解决方案可用于显示图像,最好使用 UICollectionView 仅加载显示图像的图像屏幕。你去寻找任何可以告诉你如何做的好的教程,这里是reference
【讨论】:
对不起,它不起作用我使用 UIColllectionView , UITabelview 和 Scollview 都是相同的结果。我已经从文档目录中获取图像【参考方案4】:也许 NSCache 可以帮助你。
1 当您在索引 10 处显示图像时。您可以将图像 5-9 和 11-16 缓存到 NSCache/
2 当scrollView开始滚动时,从NSCache中获取你需要的图片,并移除你不需要显示的对象。
3 我认为这可能是有效的。 Apple 提供了一个演示,他们如何使用缓存和 UICollectionView 处理海量资产。你看看它。
https://developer.apple.com/library/ios/samplecode/UsingPhotosFramework/Introduction/Intro.html
【讨论】:
【参考方案5】:我也遇到了同样的问题,我用下面的简单线解决了这个问题。
instead of using this:
[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 获取图像并不意味着您应该;此方法是同步的,因此在这种情况下绝不应从主线程调用。
是的,这就是问卷有问题的原因。此内存将被释放。其次,使用 URL 取决于问卷逻辑。它只是一个选项伙伴。还有一件事。您是否刚刚投反对票并对此发表评论以证明您投反对票的合理性?
您说的是“不消耗内存”,而不是“释放后不会留在内存中”。有很大的不同。至于“远程 URL”这件事,如果您不关心软件的质量,它只是一个选项。 Apple 关于类似方法的文档 ([NSData dataWithContentsOfURL:]
)明确说不要这样做。
你不能只编辑我的答案而不是投反对票吗...?【参考方案6】:
你可以试试 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]”。我认为它显示重复的图像。【参考方案7】:使用@autoreleasepool。以下是有关@autorelaspool 的详细信息
@autoreleasepool 语句的作用与以前相同,而不是使用 NSAutoreleasePool 类。 NSAutoreleasePool 的工作方式有点奇怪,因为创建它会在整个应用程序中产生影响; @autoreleasepool 创建一个范围区域,并使其更清楚池内的内容以及它何时耗尽(当它超出范围时)。根据 Apple 的说法,它的效率也更高。
自动释放池的概念很简单,只要一个对象实例被标记为自动释放(例如 NSString* str = [[[NSString alloc] initWithString:@"hello"] autorelease];),它就会有一个retain在那个时刻计数 +1,但在运行循环结束时,池被耗尽,任何标记为 autorelease 的对象的保留计数都会减少。这是一种在您准备任何可以为自己保留的东西时保留对象的方法。
使用 ARC,虽然开发人员不使用 autorelease 关键字,但管理 ARC 的底层系统会为您插入该关键字。 (请记住:ARC 所做的只是在适当的时候为您插入保留、释放和自动释放调用)。因此,现有的 AutoreleasePool 概念需要保留。
如果您删除自动释放池,您的对象将开始泄漏 更多详情check here
【讨论】:
以上是关于收到内存警告然后崩溃的主要内容,如果未能解决你的问题,请参考以下文章