I'm writing an app in which the user takes a photo of them self, and then goes through a series of views to adjust the image using a navigation controller. This works perfectly fine if the user takes the photo with the front camera (set as default on devices that support it), but when I repeat the process I get about half way through and it crashes after throwing a memory warning.
After profiling in Instruments I see that my apps memory footprint holds at about 20-25 MB when using the lower resolution front camera image, but when using the back camera every view change adds another 33 MB or so until it crashes at about 350 MB (on a 4S)
Below is the code I'm using to handle saving the photo to the documents directory, and then reading that file location to set the image to a UIImageView
. The "read" portion of this code is recycled through several view controllers (viewDidLoad) to set the image I saved as the background image in each view as I go.
I have removed all my image modification code to strip this down to the bear minimum attempting to isolate the problem, and I can't seem to find it. As it stands right now, all the app does is take a photo in the first view and then use that photo as the background image for about 10 more views, allocating as the user navigates through the view stack.
Now obviously the higher resolution photos would use more memory, but what I don't understand is that why the low resolution photos don't seem to be using more and more memory as I go, whereas the high resolution photos continuously use more and more until a crash.
How I am saving and reading the image:
- (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];
}
Now I know that I could try scaling the image before I save it, which I plan on looking into next, but I don't see why this doesn't work as is. Maybe I was falsely under the impression that ARC automatically deallocated views and their subviews when they leave the top of the stack.
Can anyone shed some light on why I'm stock piling my devices memory? (Hopefully something simple I'm completely overlooking) Did I somehow manage to throw ARC out the window?
EDIT: How I call for the image in my other views
- (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];
}
How navigation between my view controllers is established:
EDIT 2:
What my basic declarations look like:
#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
If relevant, how I call up my image picker:
- (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];
}
}
EDIT 3: I logged viewDidUnload, and it was in fact not being called so I'm now calling loadBackground
in viewWillAppear
and making my backgroundImageView
nil in viewDidDisappear
. I expected this to help but it made no difference.
- (void)viewWillAppear:(BOOL)animated
{
[self loadBackground];
}
- (void)viewDidDisappear:(BOOL)animated
{
NSLog(@"ViewDidDisappear");
backgroundImageView = nil;
}