问题
Somewhere:
if([MFMailComposeViewController canSendMail])
{
MFMailComposeViewController *email_vc = [[MFMailComposeViewController alloc] init];
email_vc.mailComposeDelegate = self;
[email_vc setSubject:subject];
[email_vc setMessageBody:message isHTML:FALSE];
[email_vc setToRecipients:recipients];
[self presentModalViewController:email_vc animated:FALSE];
[[UIApplication sharedApplication] setStatusBarHidden:TRUE];
[email_vc release];
}
else
...
Somewhere else:
- (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error
{
switch (result)
{
case MFMailComposeResultCancelled:
NSLog(@"Cancelled");
break;
case MFMailComposeResultSaved:
NSLog(@"Saved");
break;
case MFMailComposeResultSent:
NSLog(@"Sent");
break;
case MFMailComposeResultFailed:
NSLog(@"Compose result failed");
break;
default:
NSLog(@"Default: Cancelled");
break;
}
// This ugly thing is required because dismissModalViewControllerAnimated causes a crash
// if called right away when "Cancel" is touched.
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.01 * NSEC_PER_SEC), dispatch_get_current_queue(), ^
{
[self dismissModalViewControllerAnimated:FALSE];
});
}
That ugly "dispatch_after" block is the only way I can get this to work without a crash.
The context is that touching anything other than "Send" on the email compose view controller will cause a crash. Is there a way to deal with this without having to resort to this ugly band-aid? My theory on the band-aid is that an intermediate view is being presented when you touch "Cancel" to confirm that the user really wants to cancel. I am wondering if [self dismissModalViewControllerAnimated:FALSE];
is trying to dismiss a view out of sequence or something to that effect. By inserting a small delay I am theorizing that the mail compose view has time to cleanup before it is asked to go away.
I've seen a delay used in another question. The author did not go into any details though:
Crash On MFMailComposeViewController For iPad
EDIT 1: Adding crash log
Incident Identifier: ****************
CrashReporter Key: *****************
Hardware Model: iPhone4,1
Process: ************* [9038]
Path: /var/mobile/Applications/*********************
Identifier: ***********************
Version: ??? (???)
Code Type: ARM (Native)
Parent Process: launchd [1]
Date/Time: 2012-07-20 11:25:53.704 -0700
OS Version: iPhone OS 5.0.1 (9A405)
Report Version: 104
Exception Type: EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: KERN_INVALID_ADDRESS at 0xa003853a
Crashed Thread: 0
Thread 0 name: Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0 libobjc.A.dylib 0x316b9fbc 0x316b6000 + 16316
1 UIKit 0x350caa9e 0x34f8e000 + 1297054
2 UIKit 0x34fa6814 0x34f8e000 + 100372
3 UIKit 0x34fabfb2 0x34f8e000 + 122802
4 QuartzCore 0x33354ba0 0x33329000 + 179104
5 libdispatch.dylib 0x37896f74 0x37894000 + 12148
6 CoreFoundation 0x37bac2d6 0x37b20000 + 574166
7 CoreFoundation 0x37b2f4d6 0x37b20000 + 62678
8 CoreFoundation 0x37b2f39e 0x37b20000 + 62366
9 GraphicsServices 0x376adfc6 0x376aa000 + 16326
10 UIKit 0x34fbf73c 0x34f8e000 + 202556
11 ***************** 0x00002346 main (main.m:14)
12 ***************** 0x00002304 start + 32
EDIT 2: After much head scratching it appears that this is a genuine Apple bug.
I downloaded and ran the MailComposer sample project:
http://developer.apple.com/library/ios/#samplecode/MailComposer/Introduction/Intro.html
It works fine.
Then I edited the code to remove the animation while presenting and dismissing the mail composition controller.
[self presentModalViewController:picker animated:FALSE];
and
[self dismissModalViewControllerAnimated:FALSE];
Sure-enough, it crashed when "Cancel" was used to dismiss the email composition UI.
Running zombie brought this out:
-[MFMailComposeController actionSheet:didDismissWithButtonIndex:]: message sent to deallocated instance 0x7479ef0
I guess the action sheet gets the dismiss message instead of the mail compose view controller.
If someone could confirm behavior I'll report the bug.
EDIT 3: Bug reported.
The answer I accepted has a good explanation of the potential mechanism that is causing this issue. Also, during the back and forth in the answer comments two additional work-arounds were identified. All band-aids but now there are a few choices.
I haven't checked yet, but I suspect that ShareKit is subject to this bug as well (if the presentation of the mail compose view controller is not animated).
回答1:
I guess the action sheet gets the dismiss message instead of the mail compose view controller.
Not quite.
The sequence of events probably happens like this:
- Action sheet calls
-actionSheet:clickedButtonAtIndex:
on its delegate (the MFMCVC).- MFMailComposeViewController calls
-mailComposeController:didFinishWithResult:error:
on its delegate (your VC)- Your VC calls
[self dismissModalViewControllerAnimated:NO]
- This causes the MFMCVC to be released. Since the dismiss isn't animated, there is no longer anything referring to the MFMCVC. It gets dealloced!
- Your VC calls
- MFMailComposeViewController calls
- Action sheet calls
-actionSheet:didDismissWithButtonIndex:
on its delegate- But its delegate has been dealloced!
- So it crashes!
- But its delegate has been dealloced!
The fix would be for Apple to do actionSheet.delegate = nil
in -dealloc
.
A potential workaround
[[self.modalViewController retain] autorelease]
[self dismissModalViewControllerAnimated:NO]
This is a bit trickier to do if you are using ARC.
回答2:
this works for me:
- (void) mailComposeController: (MFMailComposeViewController *) controller
didFinishWithResult: (MFMailComposeResult) result
error: (NSError *) error {
if(result == MFMailComposeResultSent){
[self dismissViewControllerAnimated:YES completion:NULL];
} else if (result == MFMailComposeResultCancelled) {
[self dismissViewControllerAnimated:YES completion:NULL];
}
}
来源:https://stackoverflow.com/questions/11584607/how-to-prevent-crash-on-cancel-of-mfmailcomposeviewcontroller