Iphone 内存警告在使用图像创建图层时导致崩溃
我正在创建一个应用程序,它每秒拍摄一张照片并将图像保存到磁盘。 当拍摄了足够多的照片后,应用程序将使用AVFoundation框架创建图像的视频。这是通过为每个图像创建一个层来完成的,添加一个动画,该动画在一段时间后隐藏该层,然后将这些层组合成一个层,该层由Iphone 内存警告在使用图像创建图层时导致崩溃,iphone,objective-c,avfoundation,calayer,Iphone,Objective C,Avfoundation,Calayer,我正在创建一个应用程序,它每秒拍摄一张照片并将图像保存到磁盘。 当拍摄了足够多的照片后,应用程序将使用AVFoundation框架创建图像的视频。这是通过为每个图像创建一个层来完成的,添加一个动画,该动画在一段时间后隐藏该层,然后将这些层组合成一个层,该层由AVVideoCompositionCoreAnimationTool保存。 创建保存动画工具的AVVideoComposition后,我将使用avassetextortsession导出AVMutableComposition 然而,在创建
AVVideoCompositionCoreAnimationTool
保存。
创建保存动画工具的AVVideoComposition
后,我将使用avassetextortsession
导出AVMutableComposition
然而,在创建图层时,我的应用程序会给出两个memeory警告,如果我有50-60张或更多照片,则会崩溃。我使用下面的代码创建图层。这意味着对于60个图像,下面的循环将运行60次。每个图像一次。使用照片的内容创建imageLayer
,动画应用于该层,然后将该层作为子层添加到层
CALayer *layer = [CALayer layer];
layer.frame = CGRectMake(0.0f, 0.0f, renderSize.width, renderSize.height);
layer.bounds = CGRectMake(0.0f, 0.0f, renderSize.width, renderSize.height);
for (NSString *imagePath in imagePaths)
{
CMTime endTime = CMTimeAdd(cursorTime, CMTimeMake(1.0f, (CGFloat)fps));
UIImage *theImage = [[UIImage alloc] initWithContentsOfFile:imagePath];
CGFloat scaleFactor = MIN(renderSize.width / theImage.size.width, renderSize.height / theImage.size.height);
CGSize newSize = CGSizeMake(theImage.size.width * scaleFactor, theImage.size.height * scaleFactor);
UIImage *resizedImage = [theImage resizedImageToSize:newSize];
CGImageRef image = resizedImage.CGImage;
CGSize imageSize = resizedImage.size;
CALayer *imageLayer = [CALayer layer];
imageLayer.frame = CGRectMake((renderSize.width - imageSize.width) * 0.50f, (renderSize.height - imageSize.height) * 0.50f, imageSize.width, imageSize.height);
imageLayer.bounds = CGRectMake((renderSize.width - imageSize.width) * 0.50f, (renderSize.height - imageSize.height) * 0.50f, imageSize.width, imageSize.height);
imageLayer.contents = (__bridge id)image;
imageLayer.beginTime = (CGFloat)cursorTime.value / (CGFloat)cursorTime.timescale;
CABasicAnimation *hideAnimation = [CABasicAnimation animationWithKeyPath:@"hidden"];
hideAnimation.fromValue = @(NO);
hideAnimation.toValue = @(YES);
hideAnimation.additive = NO;
hideAnimation.removedOnCompletion = NO;
hideAnimation.beginTime = (CGFloat)endTime.value / (CGFloat)endTime.timescale;
hideAnimation.fillMode = kCAFillModeBoth;
[imageLayer addAnimation:hideAnimation forKey:nil];
[layer addSublayer:imageLayer];
cursorTime = endTime;
}
似乎是这段代码占用了太多内存。谁能告诉我如何优化代码或以任何其他方式避免内存警告并最终避免崩溃
根据Daij Djans的回答更新
自动释放池没有改变任何事情。我试着写一个自定义图层,然后自己画。这确实允许我渲染更多图像,但它仍然会发出内存警告并导致应用程序崩溃。 事实证明,除非在层上调用了
-setNeedsDisplay
,否则不会调用-drawInContext:
。该类如下所示:
光电层
#import <QuartzCore/QuartzCore.h>
@interface PhotoLayer : CALayer
@property (nonatomic, copy) NSString *path;
@end
当imagePath的数量较大时,对于加载原始图像的每个imagePath,制作一个新的调整大小的图像、一个层和一个动画 这是你随着时间积累的大量记忆
@autoreleasepool{…}
@MyURLLayer : CALayer
@property(copy) NSString *path;
@end
@implementation MyURLLayer
- (void)drawInContext:(CGContextRef)ctx {
//load the image and draw it!
//...
}
@end
顺便说一句,如果您不使用arc,您正在泄漏原始图像,这是您的问题。这将永远无法处理大量图像。AVVideoCompositionCoreAnimationTool要求创建动画,该动画将引用并保存CALayer中的图像数据。在AVAssetExportSession要么取消、失败,要么完成并被释放(或者您的应用程序很可能从看门狗关闭) 不幸的是,这只是导出会话的限制。有趣的是,AVAsset在被访问之前不会将其数据加载到内存中,但动画工具会这样做
解决这个问题的唯一方法是使用AVAssetWriter之类的软件将图像转换为视频,然后在完成AVAssetExportSession后,根据需要生成AVAssetExportSession。autoreleasepool没有改变任何内容。我自己尝试将图像绘制到图层。这允许我渲染更多图像,但最终我将重新渲染接收内存警告,应用程序将崩溃。结果表明,除非在层上调用了
-setNeedsDisplay
,否则不会调用-drawInContext:
。我更新了我的问题,以显示我是如何实现CALayer
子类的。如果你有任何其他想法,请让我来这里。我真的被困在这个问题上了。还有显然,我的代码有问题。仪器显示,在创建层时,应用程序会使用越来越多的内存。我想这是意料之外的。在图像上,小尖峰是在拍摄图像时出现的。然后渲染阶段开始,应用程序会继续使用内存。似乎是CGContextDrawImage
没有释放它的记忆。这听起来对吗?有什么办法可以避免吗?
CALayer *layer = [CALayer layer];
layer.frame = CGRectMake(0.0f, 0.0f, renderSize.width, renderSize.height);
layer.bounds = CGRectMake(0.0f, 0.0f, renderSize.width, renderSize.height);
for (NSString *imagePath in imagePaths)
{
@autoreleasepool {
CMTime endTime = CMTimeAdd(cursorTime, CMTimeMake(1.0f, (CGFloat)fps));
PhotoLayer *photoLayer = [[PhotoLayer alloc] init];
photoLayer.path = imagePath;
photoLayer.frame = CGRectMake(0.0f, 0.0f, renderSize.width, renderSize.height);
photoLayer.bounds = CGRectMake(0.0f, 0.0f, renderSize.width, renderSize.height);
photoLayer.beginTime = (CGFloat)cursorTime.value / (CGFloat)cursorTime.timescale;
CABasicAnimation *hideAnimation = [CABasicAnimation animationWithKeyPath:@"hidden"];
hideAnimation.fromValue = @(NO);
hideAnimation.toValue = @(YES);
hideAnimation.additive = NO;
hideAnimation.removedOnCompletion = NO;
hideAnimation.beginTime = (CGFloat)endTime.value / (CGFloat)endTime.timescale;
hideAnimation.fillMode = kCAFillModeBoth;
[photoLayer addAnimation:hideAnimation forKey:nil];
[layer addSublayer:photoLayer];
[photoLayer setNeedsDisplay];
cursorTime = endTime;
}
}
CALayer *parentLayer = [CALayer layer];
CALayer *videoLayer = [CALayer layer];
parentLayer.frame = CGRectMake(0.0f, 0.0f, renderSize.width, renderSize.height);
videoLayer.frame = CGRectMake(0.0f, 0.0f, renderSize.width, renderSize.height);
[parentLayer addSublayer:videoLayer];
[parentLayer addSublayer:layer];
@MyURLLayer : CALayer
@property(copy) NSString *path;
@end
@implementation MyURLLayer
- (void)drawInContext:(CGContextRef)ctx {
//load the image and draw it!
//...
}
@end