MFMailComposeViewController在iOS5中dismissModalViewControllerAnimated时崩溃

4

我在我的代码中使用MFMailComposeViewController提供邮件功能,但是在发送邮件或者取消邮件时会崩溃。

以下是我的代码:

(IBAction)FnForPlutoSupportEmailButtonPressed:(id)sender {
{
    if ([MFMailComposeViewController canSendMail])
    {
        MFMailComposeViewController *mailer = [[MFMailComposeViewController alloc] init];

        mailer.mailComposeDelegate = self;

        [mailer setSubject:@"Need help from Pluto support team"];

        NSArray *toRecipients = [NSArray arrayWithObjects:@"support@myplu.to",nil];
        [mailer setToRecipients:toRecipients];


        NSString *emailBody = @"";

        [mailer setMessageBody:emailBody isHTML:NO];

        //mailer.modalPresentationStyle = UIModalPresentationPageSheet;

        [self presentModalViewController:mailer animated:YES];

    }
    else
    {
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Failure"
                                                        message:@"Your device doesn't support the composer sheet"
                                                       delegate:nil
                                              cancelButtonTitle:@"OK"
                                              otherButtonTitles: nil];
        [alert show];
    }
} }

    (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error
{
// Notifies users about errors associated with the interface    
switch (result)
    {       case MFMailComposeResultCancelled:
                        break;
                    case MFMailComposeResultSaved:
                        break;
                    case MFMailComposeResultSent:
                        break;
                    case MFMailComposeResultFailed:
                        break;
                    default:
                        break;
    } 
    [self dismissModalViewControllerAnimated:YES];
     }

我已经阅读了所有博客文章,但没有找到解决方案。这篇博客文章对此有很好的解释,但根据这篇文章,我没有在viewdidload或viewdidappear中呈现我的视图控制器。
我遇到了EXE_BAD_ACCESS错误,以下是崩溃日志:**
> #0  0x00000000 in ?? ()
> #1  0x01dc5aa4 in -[UIViewController _setViewAppearState:isAnimating:] ()
> #2  0x01dc5f47 in -[UIViewController __viewDidDisappear:] ()
> #3  0x01dc6039 in -[UIViewController _endAppearanceTransition:] ()
> #4  0x01dd2e7e in -[UIViewController(UIContainerViewControllerProtectedMethods) endAppearanceTransition] ()
> #5  0x01fc8de1 in -[UIWindowController transitionViewDidComplete:fromView:toView:] ()
> #6  0x01da334b in -[UITransitionView notifyDidCompleteTransition:] ()
> #7  0x01da3070 in -[UITransitionView _didCompleteTransition:] ()
> #8  0x01da531b in -[UITransitionView _transitionDidStop:finished:] ()
> #9  0x01d23fb6 in -[UIViewAnimationState sendDelegateAnimationDidStop:finished:] ()
> #10 0x01d24154 in -[UIViewAnimationState animationDidStop:finished:] ()
> #11 0x0163bbce in CA::Layer::run_animation_callbacks ()
> #12 0x03664fe4 in _dispatch_client_callout ()
> #13 0x03655997 in _dispatch_main_queue_callback_4CF ()
> #14 0x012c03b5 in __CFRunLoopRun ()
> #15 0x012bf804 in CFRunLoopRunSpecific ()
> #16 0x012bf6db in CFRunLoopRunInMode ()
> #17 0x030f1913 in GSEventRunModal ()
> #18 0x030f1798 in GSEventRun ()
> #19 0x01ce82c1 in UIApplicationMain ()

根据苹果发布的 iOS 5 的最新文档,他们提到:

presentModalViewController:animated:

向用户展示给定视图控制器管理的模态视图。(已弃用。请使用 presentViewController:animated:completion: 代替。)

- (void)presentModalViewController:(UIViewController *)modalViewController animated:(BOOL)animated
Parameters

关闭由接收器呈现的视图控制器。(已弃用。请改用dismissViewControllerAnimated:completion:)。

- (void)dismissModalViewControllerAnimated:(BOOL)animated

我也尝试了这个方法,但它仍然会崩溃。

请查看带有更新崩溃日志的问题。 - Gaurav
你的代码对我来说运行得非常完美,首先想问一下你的导入看起来怎么样?并且你是否设置了MFMailComposeViewControllerDelegate? - Mick MacCallum
是的,我正在设置委托并且导入也正确。 - Gaurav
哇 - 谁会知道你在使用SHKSwizzle - 你以前从未提到过,现在这就是答案!?! - David H
抱歉,David,我从没想过SHKSwizzle会给我带来麻烦。谢谢你的帮助,伙计。 - Gaurav
显示剩余2条评论
7个回答

5
您需要在类中保持对 MFMailComposeViewController *mailer 的强引用,然后在您关闭它之后可以将该引用设置为 null。问我怎么知道的 :-)
@implememtation MyClass
{
    MFMailComposeViewController *mailer;
}
...

(IBAction)FnForPlutoSupportEmailButtonPressed:(id)sender {
{
    if ([MFMailComposeViewController canSendMail])
    {
        /* USE IVAR */mailer = [[MFMailComposeViewController alloc] init];

稍后,当完全完成它时,您只需使用“mailer = nil;”来释放它。
编辑:我的建议是使用块到主队列进行发布。如果您只使用“self.mailer = nil”,则在最终委托方法完成后才会发生释放,并且您肯定不再使用它。
编辑2:这个崩溃并不总是发生在所有设备上 - 我会说这是它接收到最终委托方法和完成其工作之间的一种竞争条件。苹果并没有关于保持引用的任何说明 - 但是,在苹果产品的一般实践中,假定您获得的任何对象都是通过一个runLoop“借出的”,如果此后要保留引用,则必须保留该对象。

1
我用这个尝试了,但仍然崩溃了,请您简要解释一下。 - Gaurav
1
David,我按照你建议的做了,但仍然没有成功。 - Gaurav
在“didFinishWithResult:”块中添加一些NSLog,并在关闭之前记录“self”和“Controller)”。将dismiss调用更改为一个block:dispatch_async(dispatch_get_main_queue(), ^{ [self presentModalViewController:mailer animated:YES];} );(在文本编辑器中编写)。 这样做的想法是不要在其委托回调中关闭模态视图。如果这不起作用,请确保主视图控制器由推送或实例化它的任何人都具有对它的强引用。 - David H
在“didFinishWithResult:”块中添加一些NSLog,并在dismiss之前记录“self”和“Controller”。将dismiss调用更改为块:dispatch_async(dispatch_get_main_queue(), ^{ [self presentModalViewController:mailer animated:YES];} );(在文本编辑器中编写)。这样做的想法是不要在其委托回调中解除模态视图。如果这样做不起作用,请确保您的主视图控制器由推动或实例化它的人具有强引用。 - David H
David H,我知道你之前回答过这个问题,但是我现在有点困惑在哪里解除模态视图控制器;目前它是在mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error中解除的。看起来你可能在暗示这不是做这件事的正确位置。我将我的邮件方法放在一个帮助类中,该类具有MFMailComposeViewController作为强引用属性,在该方法的末尾,我调用[self.delegateViewController dismissModalViewControllerAnimated:YES]。 - SAHM
我所做的并建议的是使用块到主队列来完成此操作。如果你只是使用“myController = nil”,它会实现你想要的效果,因为它会转换成“self->myController = nil;”,从而释放发生。 - David H

3
如果你的代码中实现了ShareKit框架,请打开SHK.m并进行更改。
        [[currentView parentViewController] dismissModalViewControllerAnimated:YES];

为了

        [currentView  dismissModalViewControllerAnimated:YES];

这将解决您的问题。

感谢大家的回应。

并且评论这些行:

SHKSwizzle([MFMailComposeViewController class], @selector(viewDidDisappear:), @selector(SHKviewDidDisappear:));
if (NSClassFromString(@"MFMessageComposeViewController") != nil) SHKSwizzle([MFMessageComposeViewController class], @selector(viewDidDisappear:), @selector(SHKviewDidDisappear:));

2

你可以使用以下代码发送邮件,但模拟器不会发送任何电子邮件,您必须在设备上安装应用程序。当您尝试访问未在内存中的对象时,将出现EXE_BAD_ACCESS错误,并且请记住设备区分大小写。

-(void)sendMail:(id)sender
{
    //create instance of class at runtime.
    Class mailComposer = (NSClassFromString(@"MFMailComposeViewController"));
if (mailClass != nil)
{
        // check the current device is configured for sending emails
        //if it is not configured for mail you open mail application on device.
    if ([mailClass canSendMail])
    {
        [self displayComposerSheet];
    }
    else
    {
        [self launchMailAppOnDevice];
    }
}
else
{
    [self launchMailAppOnDevice];
}

}
-(void)displayComposerSheet 
 {
MFMailComposeViewController *mailPicker = [[MFMailComposeViewController alloc] init];
picker.mailComposeDelegate = self;

[picker setSubject:@"Welcome!"];

    //recipients

NSArray *to = [NSArray arrayWithObject:@"jhon@example.com"];  

[picker setToRecipients:toRecipients];

    // Attach an image
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"Default" ofType:@"png"];
NSData *imageData = [NSData dataWithContentsOfFile:filePath];
[picker addAttachmentData:myData mimeType:@"image/png" fileName:@"Default"];

    // Set email body text
    //NSString *emailBody = @"......!";
    //[picker setMessageBody:emailBody isHTML:NO];

[self presentModalViewController:picker animated:YES];
[picker release];
}


- (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:   (MFMailComposeResult)result error:(NSError*)error 
{   
message.hidden = NO;
    // Notifies users about errors associated with the interface
switch (result)
{

    case MFMailComposeResultCancelled:
        message.text = @"Canceled";
        break;
    case MFMailComposeResultSaved:
        message.text = @"Saved";
        break;
    case MFMailComposeResultSent:
        message.text = @"Sent";
        break;
    case MFMailComposeResultFailed:
        message.text = @"Failed";
        break;
    default:
        message.text = @"Not sent";
        break;
}
[self dismissModalViewControllerAnimated:YES];
}

-(void)launchMailAppOnDevice
{
NSString *recipients = @"";
NSString *body = @"";

NSString *email1 = [NSString stringWithFormat:@"%@%@", recipients, body];
email1 = [email1 stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];

[[UIApplication sharedApplication] openURL:[NSURL URLWithString:email1]];
}

Class mailComposer = (NSClassFromString(@"MFMailComposeViewController")) Yuk,NSClassFromString是Objective-C的一个很棒的特性,但这并不是它应该使用的地方。 - Jonathan King

2

由于堆栈的停止位是 0x00000000,我认为UIKit内部的某些代码正在跳转到空函数指针。启用Zombies并查看是否出现僵尸对象访问错误。(编辑您的项目方案,在运行/调试下的诊断选项卡中勾选“启用僵尸对象”)


更好的方法是使用Instruments.app对您的应用程序进行分析,使用分配工具,并在选项中启用僵尸对象。 - nielsbot

1

尝试这段代码,它可以很好地运行而不会崩溃。联系我们的方法是按钮目标选择器。

    -(void)ContactUs:(UIButton*)button
    {
        Class mailClass = (NSClassFromString(@"MFMailComposeViewController"));
        if (mailClass != nil)
        {
            // We must always check whether the current device is configured for sending emails
            if ([mailClass canSendMail])
            {
                [self displayComposerSheet];
            }
            else
            {
                [self launchMailAppOnDevice];
            }
        }
        else
        {
            [self launchMailAppOnDevice];
        }

    }

-(void)displayComposerSheet 
{
    MFMailComposeViewController *picker = [[MFMailComposeViewController alloc] init];
    picker.mailComposeDelegate = self;

    //[picker setSubject:@"Hello from California!"];

    // Set up recipients

    NSArray *toRecipients = [NSArray arrayWithObject:@"info@imp.co.in"]; 
    //NSArray *ccRecipients = [NSArray arrayWithObjects:@"second@example.com", @"third@example.com", nil]; 
    //NSArray *bccRecipients = [NSArray arrayWithObject:@"fourth@example.com"]; 

    [picker setToRecipients:toRecipients];
    //[picker setCcRecipients:ccRecipients];    
    //[picker setBccRecipients:bccRecipients];

    // Attach an image to the email
    /*NSString *path = [[NSBundle mainBundle] pathForResource:@"rainy" ofType:@"png"];
     NSData *myData = [NSData dataWithContentsOfFile:path];
     [picker addAttachmentData:myData mimeType:@"image/png" fileName:@"rainy"];*/

    // Fill out the email body text
    //NSString *emailBody = @"It is raining in sunny California!";
    //[picker setMessageBody:emailBody isHTML:NO];

    [self presentModalViewController:picker animated:YES];
    [picker release];
}
- (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error 
{   
    message.hidden = NO;
    // Notifies users about errors associated with the interface
    switch (result)
    {

        case MFMailComposeResultCancelled:
            message.text = @"Canceled";
            break;
        case MFMailComposeResultSaved:
            message.text = @"Saved";
            break;
        case MFMailComposeResultSent:
            message.text = @"Sent";
            break;
        case MFMailComposeResultFailed:
            message.text = @"Failed";
            break;
        default:
            message.text = @"Not sent";
            break;
    }
    [self dismissModalViewControllerAnimated:YES];
}

-(void)launchMailAppOnDevice
{
    NSString *recipients = @"";
    NSString *body = @"";

    NSString *email1 = [NSString stringWithFormat:@"%@%@", recipients, body];
    email1 = [email1 stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];

    [[UIApplication sharedApplication] openURL:[NSURL URLWithString:email1]];
}

这也导致了相同的崩溃。 - Gaurav
1
这对我来说完美地工作了,Gaurav。这个函数在哪里崩溃了?确切地说。 - iMeMyself
这个在ios4上工作正常,但在ios5及以上版本中会崩溃。 - Gaurav
1
这应该对你完美地工作。你启用了任何断点来查看哪一行代码导致崩溃了吗? - user717452

1

如果您正在使用 ARC,则在调用 dismiss 之前,控制器对象将已被释放。

请将该对象保留在除堆栈分配变量之外的其他位置。


0

你在 .h 文件中设置了 MFMailComposeViewControllerDelegate 代理吗?我以前遇到过这样的问题。而且错误非常小。请检查 .h 文件中的代理。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接