【发布时间】:2012-05-20 18:32:53
【问题描述】:
我正在编写一个应用程序,在该应用程序中,用户为他们自己拍照,然后通过一系列视图使用导航控制器调整图像。如果用户使用前置摄像头拍摄照片(在支持它的设备上设置为默认设置),这非常有效,但是当我重复该过程时,我完成了大约一半,并在引发内存警告后崩溃。
在 Instruments 中进行分析后,我发现我的应用程序内存占用在使用较低分辨率的前置摄像头图像时保持在 20-25 MB 左右,但在使用后置摄像头时,每次视图更改都会再增加 33 MB 左右,直到它崩溃350 MB(在 4S 上)
下面是我用来处理将照片保存到文档目录的代码,然后读取该文件位置以将图像设置为UIImageView。这段代码的“读取”部分通过几个视图控制器(viewDidLoad)循环使用,以便将我保存的图像设置为每个视图中的背景图像。
我已经删除了我所有的图像修改代码,以将其剥离到最小限度,试图隔离问题,但我似乎找不到它。就目前而言,该应用所做的只是在第一个视图中拍摄一张照片,然后将该照片用作大约 10 个视图的背景图像,并在用户浏览视图堆栈时进行分配。
现在显然更高分辨率的照片会使用更多的内存,但我不明白为什么低分辨率的照片似乎没有使用越来越多的内存,而高分辨率的照片却持续使用越来越多,直到崩溃。
我如何保存和读取图像:
- (void) imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
UIImage *image = [info objectForKey:@"UIImagePickerControllerOriginalImage"];
jpgData = UIImageJPEGRepresentation(image, 1);
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsPath = [paths objectAtIndex:0];
filePath = [documentsPath stringByAppendingPathComponent:@"image.jpeg"];
[jpgData writeToFile:filePath atomically:YES];
[self dismissModalViewControllerAnimated:YES];
[disableNextButton setEnabled:YES];
jpgData = [NSData dataWithContentsOfFile:filePath];
UIImage *image2 = [UIImage imageWithData:jpgData];
[imageView setImage:image2];
}
现在我知道我可以在保存之前尝试缩放图像,我计划接下来研究它,但我不明白为什么这不起作用。也许我错误地认为 ARC 会在视图及其子视图离开堆栈顶部时自动释放它们。
谁能解释我为什么要囤积设备内存? (希望我完全忽略了一些简单的事情)我是否设法将 ARC 扔出窗外?
编辑:我如何在其他视图中调用图像
- (void)loadBackground
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsPath = [paths objectAtIndex:0];
NSString *filePath = [documentsPath stringByAppendingPathComponent:@"image.jpeg"];
UIImage *image = [UIImage imageWithContentsOfFile:filePath];
[backgroundImageView setImage:image];
}
我的视图控制器之间的导航是如何建立的:
编辑 2:
我的基本声明是什么样的:
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
@interface PhotoPickerViewController : UIViewController <UIImagePickerControllerDelegate, UINavigationControllerDelegate>
{
IBOutlet UIImageView *imageView;
NSData *jpgData;
NSString *filePath;
UIImagePickerController *imagePicker;
IBOutlet UIBarButtonItem *disableNextButton;
}
@end
如果相关,我如何调用我的图像选择器:
- (void)callCameraPicker
{
if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera] == YES)
{
NSLog(@"Camera is available and ready");
imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
imagePicker.delegate = self;
imagePicker.allowsEditing = NO;
imagePicker.cameraCaptureMode = UIImagePickerControllerCameraCaptureModePhoto;
NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]; for (AVCaptureDevice *device in devices)
{
if([[UIScreen mainScreen] respondsToSelector:@selector(scale)] && [[UIScreen mainScreen] scale] == 2.0)
{
imagePicker.cameraDevice = UIImagePickerControllerCameraDeviceFront;
}
}
imagePicker.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentModalViewController:imagePicker animated:YES];
}
else
{
NSLog(@"Camera is not available");
UIAlertView *cameraAlert = [[UIAlertView alloc] initWithTitle:@"Error"
message:@"Your device doesn't seem to have a camera!"
delegate:self cancelButtonTitle:@"Dismiss"
otherButtonTitles:nil];
[cameraAlert show];
}
}
编辑 3: 我记录了 viewDidUnload,实际上它没有被调用,所以我现在在 viewWillAppear 中调用 loadBackground 并在 viewDidDisappear 中设置我的 backgroundImageView nil。我希望这会有所帮助,但没有任何区别。
- (void)viewWillAppear:(BOOL)animated
{
[self loadBackground];
}
- (void)viewDidDisappear:(BOOL)animated
{
NSLog(@"ViewDidDisappear");
backgroundImageView = nil;
}
【问题讨论】:
-
你不应该使用
imageWithData,因为你可以使用imageWithContentsOfFile。imageWithData无法缓存,因此您将为每个图像创建一个新的字节缓冲区。 -
我可以问一下,当您已经从信息字典中的 UIImagePickerControllerOriginalImage 键中将 image2 作为 UIImage 对象时,为什么要从数据中创建 image2 作为 UIImage 对象?完成所有任务以释放内存后,您也可以取消图像和 jpgdata...
-
@JonathanCichon 不幸的是,
imageWithData或imageWithContentsOfFile都没有缓存数据。 -
你绝对不应该通过从文件中加载来创建
image2- 只需执行[imageView setImage:image]。通过第二次加载,您的内存需求增加了一倍。但这只能解释这么多-我认为我们需要在导航堆栈中查看您的 10 个视图的一些示例代码,以了解您在其中所做的事情-听起来您正在加载每个视图中的图像并以某种方式阻止 iOS释放导航堆栈中较低的那些。 -
好点,我已经对那段代码进行了更正,实际上并没有什么可显示的。我上面列出的代码(阅读部分)在我的其他视图控制器的 viewDidLoad 方法中被回收,它们只是链接到故事板中的视图控制器,设置为从一个推送到另一个。
标签: objective-c ios cocoa-touch memory uiimagepickercontroller