如何避免在取消MFMailComposeViewController时崩溃?

5

在某个地方:

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
...

在其他地方:

- (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];
    }); 

这个丑陋的“dispatch_after”块是我唯一能够在不崩溃的情况下使其工作的方法。

背景是,在电子邮件撰写视图控制器上除了“发送”以外的任何触摸都会导致崩溃。有没有办法处理这个问题而不必采用这种丑陋的应急措施?我的理论是,当您触摸“取消”以确认用户确实想要取消时,会呈现中间视图。我想知道 [self dismissModalViewControllerAnimated:FALSE]; 是否试图按顺序关闭视图或类似的操作。通过插入一个小延迟,我推测邮件撰写视图有时间进行清理,然后才被要求离开。

我在另一个问题中看到了使用延迟的方法。作者没有详细说明:

iPad 上 MFMailComposeViewController 崩溃

编辑 1:添加崩溃日志

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

编辑2: 经过长时间的思考,这似乎是一个真正的苹果bug。

我下载并运行了MailComposer示例项目:

http://developer.apple.com/library/ios/#samplecode/MailComposer/Introduction/Intro.html

它可以正常工作。

然后我编辑了代码,删除了在呈现和关闭邮件组合控制器时的动画效果。

[self presentModalViewController:picker animated:FALSE];

and

[self dismissModalViewControllerAnimated:FALSE];

当使用“取消”来关闭电子邮件编辑界面时,它确实会崩溃。

运行僵尸进程会导致此问题出现:

-[MFMailComposeController actionSheet:didDismissWithButtonIndex:]: message sent to deallocated instance 0x7479ef0

我猜测动作表单收到了退出信息,而不是邮件撰写视图控制器。
如果有人能确认这种行为,我会报告这个错误。
编辑3:已报告错误。
我接受的答案对可能导致此问题的潜在机制进行了很好的解释。此外,在答案评论中的来回讨论中还确定了两个额外的解决方法。所有都是权宜之计,但现在有了几个选择。
我还没有检查,但我怀疑ShareKit也会受到此错误的影响(如果邮件撰写视图控制器的呈现没有动画)。

动画可能会产生与您的所有额外调度代码相同的效果,还要考虑在调度代码和其前面的switch case处设置断点,以查看崩溃发生的位置。 - Eric
崩溃日志已发布。我必须承认,我还不明白一个小延迟如何修复这个问题。 - martin's
请查看我的最后一次编辑。这似乎是一个苹果的错误。 - martin's
编辑旧评论:ShareKit似乎有很多内存泄漏。如果您将视图分配给self.view样式的对象,它们不知道委托和自动释放的模式,特别是TwitterForm及其创建控制器。 - Stephen J
检查这个链接: https://dev59.com/z1HTa4cB1Zd3GeqPSYl9 在我们经过大量搜索后,这对我们很有用。为他点赞! - Stephen J
六年后,我仍在为同样的问题苦苦挣扎。推迟派遣并没有解决它。 - DoruChidean
2个回答

6
我猜测,动作表单获得的是关闭消息,而不是邮件撰写视图控制器。
不完全正确。
事件顺序可能是这样的:
- 动作表单在其代理(MFMCVC)上调用-actionSheet:clickedButtonAtIndex: - MFMailComposeViewController在其代理(您的VC)上调用-mailComposeController:didFinishWithResult:error: - 您的VC调用[self dismissModalViewControllerAnimated:NO] - 这会导致MFMCVC被释放。由于解除不是动画的,因此不再有任何引用到MFMCVC。它被释放了! - 动作表单在其代理上调用-actionSheet:didDismissWithButtonIndex: - 但它的代理已被释放! - 因此它崩溃了!
修复方法是苹果在dealloc中执行actionSheet.delegate = nil。
潜在解决方法
[[self.modalViewController retain] autorelease]
[self dismissModalViewControllerAnimated:NO]

如果你正在使用ARC,这就有点棘手了。


没有使用ARC。刚刚尝试了你提供的解决方法,但仍然崩溃。鉴于你的解释,为什么你认为延迟调用[self dismissModalViewControllerAnimated:NO]可以暂时解决这个问题? - martin's
1
也许在自动释放池被清空后(例如在下一个运行循环中)才会调用-actionSheet:didDismissWithButtonIndex:?您可以尝试使用[controller performSelector:@selector(class) withObject:nil afterDelay:1]等方式将MFMCVC保留更长时间。 - tc.
好的,这行得通。我还发现,在[self dismissModalViewControllerAnimated:NO]之前简单地加上[controller retain]也可以解决问题。在邮件撰写控制器的所有可能的dismiss模式下(使用Instruments进行了检查),都没有泄漏。我会将其标记为正确答案,因为您讨论了发生的事件序列以及这些评论中包含的两个附加解决方法(超出了简单延迟)。Bug已向Apple报告。我们将看到反馈。谢谢。 - martin's
附加说明:[[controller retain] autorelease] 不起作用。你仍然会崩溃。 - martin's
一个“裸露”的[controller retain]是一个泄漏。Leaks并不能捕获所有的泄漏。(特别是,VC被注册为通知,因此永远不会被检测到泄漏;我应该在某个时候提交一个错误报告。) - tc.

2
这对我有效:

这对我有用:

- (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];
}

}


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