I created an app in which the user uses the front camera to take pictures and emails them, and the app never goes into background for at least 8 hours; the app is displayed in an office and must be always in foreground. Everything happens in an UIViewController.
The issue is with the MFMailComposeViewController, specifically with the memory which it consumes. In Instruments - Activity Monitor there is a MailCompositionS which keeps increasing in real memory usage.
My code is:
- (void)emailPhoto
{
NSString *emailTitle;
NSString *messageBody;
MFMailComposeViewController *mc = [[MFMailComposeViewController alloc] init];
mc.mailComposeDelegate = self;
[mc setSubject:emailTitle];
[mc setMessageBody:messageBody isHTML:NO];
NSData *dataImage = UIImageJPEGRepresentation(photoView.image, 0.4);
[mc addAttachmentData:dataImage mimeType:#"image/jpeg" fileName:#"image.jpg"];
[self presentViewController:mc animated:YES completion:nil];
mc = nil;
dataImage = nil;
}
- (void) mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error
{
[self dismissViewControllerAnimated:YES completion:^{
[self backToCamera];
}];
}
I really don't understand what could be the problem. With or without the image attachment the MailCompositionS keeps increasing in memory consumption, and because the app always stays in that UIViewController and never goes into background, the memory is never released.
PS I am not detecting any leaks.
I think you have to create a once instance of MFMailComposeViewController in a property. instead of creating an instance for each Photo. just copy the couples of lines.
self.mc = [[MFMailComposeViewController alloc] init];
mc.mailComposeDelegate = self; in
viewDidLoad() or viewWillAppear().
Good Luck
Related
I have a singleton and in it I create a custom method that is going to be used by multiple viewcontrollers. The method is to display an email composer.
-(void)emailSend:(NSString*)bodyStr inVC:(UIViewController*)vc {
if ([MFMailComposeViewController canSendMail]) {
NSString *messageBody = bodyStr;
MFMailComposeViewController *mc = [[MFMailComposeViewController alloc] init];
mc.mailComposeDelegate = vc; // <-- warning
[mc setSubject:#"Say Hello"];
[vc presentViewController:mc animated:YES completion:NULL];
}else{
// Not setup
}
}
On other viewcontrollers I call this by:
[[MySingle singleton] emailSend:#"Testing" inVc:self];
The warning message is assigning to
id __Nullable from incompatible
type UIViewController *__strong
Any way how to make it work?
You need to make some changes on your method :
From :
-(void)emailSend:(NSString*)bodyStr inVC:(UIViewController*)vc
To :
-(void)emailSend:(NSString*)bodyStr inVC:(id)vc
I have a UICollectionView and a bar button at top right(CameraViewController1 : UICollectionViewController).The flow is when I take a picture it moves to a new view controller where the image can be cropped.User has two option Use and Cancel after choosing any of this option it gets back the image to the collection view and it gets arranged like cells.I want to take many photos.But I can take up to 3 pictures only where as the app crashes immediately and shows a message "App terminated due to memory pressure".But the worst part is when I tested the same app in iPhone 5 running iOS 7 the crash wasn't happen.When I test the same in iPhone 4 running iOS 7 it gets crashed and produce received memory warning.
Here my code
- (IBAction)TakeaPhoto:(id)sender {
[[UIApplication sharedApplication]setStatusBarHidden:FALSE withAnimation:NO];
gallery=0;
picker1 = [[UIImagePickerController alloc] init];
picker1.delegate = self;
self.resizeableCropArea =YES;
self.cropSize=CGSizeMake(300,350);
//picker1.allowsEditing = YES;
picker1.sourceType = UIImagePickerControllerSourceTypeCamera;
[self presentViewController:picker1 animated:YES completion:NULL];
}
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
[self dismissViewControllerAnimated:YES completion:NULL];
UIImage *image = [info objectForKey: UIImagePickerControllerOriginalImage];
image_cap = [self imageTemp:image scaledToSize:CGSizeMake(320, 370)];
dataTemp = UIImageJPEGRepresentation(image,0.0);
CropViewController *cropController = [[CropViewController alloc] init];
cropController.sourceImage = [info objectForKey:UIImagePickerControllerOriginalImage];
Original_img = UIImageJPEGRepresentation(cropController.sourceImage,0.0);
[original_image addObject:[UIImage imageWithData:Original_img]]; //original_image Nsmutablearray
NSLog(#"source image=%#",cropController.sourceImage);
cropController.resizeableCropArea = self.resizeableCropArea;
cropController.cropSize = self.cropSize;
cropController.delegate = self;
Cancel_Image= cropController.sourceImage;
[self.navigationController pushViewController:cropController animated:YES];
}
#Ramanan R R, I m totally agree with the #Rushabh's comment..
You are allocating that UIImagePickerController for many more times, as TakeaPhoto method call you are allocating UIImagePickerConrtoller, it is not necessary to allocate that multiple times. It makes memory spoilage, thats why your app is going to terminate or crash..
Just allocate that one time in viewDidLoad, make sure one more thing that, do
UIImagePickerController as a strong property, because in past it took my whole day to solve issue...
Hope this will work for you and your app will run smoothly...:)
When i try to load camera from my code, camera preview is black. If I wait for 10-20 seconds it will show real camera preview. I found several questions and some of them suggest that running some other code in background should be the reason for this. However I don't have any code running in background.
How should I fix this?
This is my code where I run camera
UIImagePickerController *photoPicker = [[UIImagePickerController alloc] init];
photoPicker.delegate = self;
photoPicker.sourceType = UIImagePickerControllerSourceTypeCamera;
[self presentViewController:photoPicker animated:YES completion:NULL];
About 5 months ago my team discovered a memory leak with UIImageViewController in iOS7. Each instantiation slowed down the app exponentially (i.e. first alloc-init had a 1 second delay, second had a 2 second delay, third had a 5 second delay). Eventually, we were having 30-60 delays (similar to what you're experiencing).
We resolved the issue by subclassing UIImagePickerController and making it a Singleton. That way it was only ever initialized once. Now our delay is minimal and we avoid the leak. If subclassing isn't an option, try a class property in your viewController and just lazy load it like so.
-(UIImagePickerController *)imagePicker{
if(!_imagePicker){
_imagePicker = [[UIImagePickerController alloc]init];
_imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
}
return _imagePicker;
}
Then you can just call it later like:
[self presentViewController:self.imagePicker animated:YES completion:nil];
Had this myself - it happens if something is running on the main dispatch thread - are you resizing images by any chance?
It puts the preview onto the main thread and if something is using it, you get a black screen. It's a bug and the workaround is to either take over the main thread or to disable the photo picker until the queue is free
This Should work for you:
- (void)cameraViewPickerController:(UIImagePickerController *)picker
{
[self startCameraControllerFromViewController: picker
usingDelegate: self];
}
- (BOOL) startCameraControllerFromViewController: (UIViewController*) controller
usingDelegate: (id <UIImagePickerControllerDelegate,
UINavigationControllerDelegate>) delegate {
if (([UIImagePickerController isSourceTypeAvailable:
UIImagePickerControllerSourceTypeCamera] == NO)
|| (delegate == nil)
|| (controller == nil))
return NO;
UIImagePickerController *cameraUI = [[UIImagePickerController alloc] init];
cameraUI.sourceType = UIImagePickerControllerSourceTypeCamera;
// Displays a control that allows the user to choose movie capture
cameraUI.mediaTypes = [[NSArray alloc] initWithObjects: (NSString *) kUTTypeImage, (NSString *) kUTTypeMovie,nil];
// Hides the controls for moving & scaling pictures, or for
// trimming movies. To instead show the controls, use YES.
cameraUI.allowsEditing = NO;
cameraUI.delegate = delegate;
[controller presentViewController:cameraUI animated:YES completion:nil];
return YES;
}
At this point I'm not sure if these leaks might be CoreData related or what, since I've experienced 48 byte strdup leaks in other parts of this same app for apparently different reasons - see my other question:another stack overflow question
But, assuming no relation, I have a viewController which, based on the user selecting an option, presents an ABPeoplePicker. However, it seems like just by presenting the picker I'm leaking, regardless of choosing a contact or not.
The code for presenting the picker is:
- (void)showPeoplePickerController
{
ABPeoplePickerNavigationController *picker = [[ABPeoplePickerNavigationController alloc] init];
picker.displayedProperties = [NSArray arrayWithObject:[NSNumber numberWithInt:kABPersonEmailProperty]];
picker.peoplePickerDelegate = self;
[self presentModalViewController:picker animated:YES];
[picker release];
}
And the delegate methods implemented as follow:
- (void)peoplePickerNavigationControllerDidCancel:(ABPeoplePickerNavigationController *)peoplePicker
{
[self dismissModalViewControllerAnimated:YES];
}
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker
shouldContinueAfterSelectingPerson:(ABRecordRef)person
{
return YES;
}
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker
shouldContinueAfterSelectingPerson:(ABRecordRef)person
property:(ABPropertyID)property
identifier:(ABMultiValueIdentifier)identifier
{
ABMultiValueRef emails = ABRecordCopyValue(person, property);
if(userEmailString)
[userEmailString release];
userEmailString = (NSString*)ABMultiValueCopyValueAtIndex(emails, identifier);
CFRelease(emails);
[[NSNotificationCenter defaultCenter] postNotificationName:#"recipientEmailDidUpdateNotification"
object:self];
return NO;
}
And just in case, userEmailString is a retained NSString property of the controller (meaning I could also go for self.userEmailString = blah).
These are screenshots from Instruments, reporting the leak. But notice that it thinks its the picker not being released, though I am calling release after presenting it. And I've also tried doing CFRelease() instead... but still the same.
Anyway, yep.. the leaks are in the SDK.
I am using MFMailComposeViewController in my app and the mail sending part seems to be OK.
But when I leave the mail app, things go wrong :
- one toolbar (UIToolbar object) has disappeared.
- one pointer (UIImageView*) has become nil, without me doing anything for that to happen.
In other words the calling environment is changed although I do not want it to change.
Where could be my mistake?
Here is my code, in case someone can see something wrong :
- (void)mailComposeController:(MFMailComposeViewController*)controller
didFinishWithResult:(MFMailComposeResult)result
error:(NSError*)error
{
[controller dismissModalViewControllerAnimated:YES];
}
-(IBAction)sendAsEMail {
MFMailComposeViewController *mailComposeViewController=[[MFMailComposeViewController alloc] init];
mailComposeViewController.mailComposeDelegate=self;
[mailComposeViewController setSubject:#"Mail subject"];
[mailComposeViewController setMessageBody:#"This is for you !" isHTML:NO];
[mailComposeViewController addAttachmentData:
[NSData dataWithContentsOfFile:[[My_ViewController getDocDir] stringByAppendingPathComponent:
[pictureNames objectAtIndex:userItemSelected]]]
mimeType:#"image/png" fileName:#"Picture.png"];
if (mailComposeViewController) [self presentModalViewController:mailComposeViewController animated:YES];
[mailComposeViewController release];
}
Thanks for any piece of relevant information.
Try out this link it explain in detail
Add framework
Then .h file header files
Then .m file the mail code
Check at this link.