【问题标题】:Click on UIAlertView crashes app if view is dismissed如果视图被关闭,请单击 UIAlertView 崩溃应用程序
【发布时间】:2011-04-25 16:40:00
【问题描述】:

如果发生错误,将显示UIAlertView。但与此同时,调用UIAlertView 的观点已被驳回(因此被释放)。如果用户单击“确定”,则应用程序崩溃,因为向已发布实例发送了消息。这将导致您的应用崩溃:

UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"test" message:@"test" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];
[alertView show];
[alertView release];
alertView = nil;
[self.navigationController popViewControllerAnimated:YES];

我认为UIAlertView 是一个独立的单元。但似乎并非如此。有没有办法避免应用程序崩溃(除了不关闭视图)?

【问题讨论】:

    标签: iphone cocoa-touch uiviewcontroller uialertview dismiss


    【解决方案1】:

    确保您正在实施UIAlertViewDelegate 协议。 如果您不关心何时解除警报,只需将委托初始化为nil

    UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"test" message:@"test" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
    

    【讨论】:

    • 我已经为此警报实现了额外的UIAlertViewDelegate 协议。似乎除了只提供一个“确定”按钮并将委托设置为零之外别无他法。我试试看。
    • 这不是一个正确的答案。 SooDesuNe 的回答是正确的。您需要不设置委托,或者如果委托将被释放,请确保取消设置。
    【解决方案2】:

    UIAlertView 被解除时,委托被调用,所以在你的情况下:

    delegate:self
    

    不保留委托,就像添加到数组中的对象或子视图一样。所以在你的情况下,当你打电话时:

    [self.navigationController popViewControllerAnimated:YES];
    

    self 最有可能被释放,当用户解除警报时,self 被调用,但已被释放,因此不再存在。

    检查这一点的一种简单方法是在selfdealloc 方法中放置一个记录器语句,例如NSLog(@"I'm gone");,如果它运行了,那么你知道你的self 不再存在,并且任何发送给它的消息会导致崩溃。

    【讨论】:

    • 很好的解释。但是我能做些什么呢?
    • 您有两个选择,1) 选择一个不同的委托,或 2) 让 self 被其他对象保留(即来自其他对象 [the-reference-to-what-were-calling- self retain]) 在不知道您在委托方法中做什么的情况下,很难说哪个是最好的。如果您不使用委托方法,请让 nil 成为委托
    • 我在下面的回答中提出了一个不同的选择:当视图被解除分配时,只需取消设置委托。
    【解决方案3】:

    如果 UIAlertView 对象可以在应用程序中的任何位置使用,而不仅仅是在当前视图上,则将其保留在应用程序中任何位置都可用的东西中,或者是整个可能的视图堆栈下的某个持久性根视图控制器,或应用委托。

    添加:

    这个顶级对象还可以保留警报视图的委托,直到它完成被需要(在警报视图解除之后)。

    【讨论】:

    • 更重要的问题是谁保留了警报视图代理?这需要持久化。
    • 由显示将其放入的 UIWindow 保留。是的,警报视图代理应该在警报视图关闭时仍然存在,或者在代理释放时设置为 nil。
    • 我也想过这个。但这对于我认为的这种特殊情况来说有点太复杂了。这只发生在用户进行两次操作时,其中一个操作因完成时间太短而失败。在其他情况下,我不需要这个。
    【解决方案4】:

    使 UIAlertView 成为视图控制器的保留属性,以便您可以在 dealloc 中引用它,然后在取消分配视图控制器时将警报视图的委托设置为 nil。

    确保在解除并释放保留的警报视图后正确释放它。

    例如:

    @interface MyViewController : UIViewController <UIAlertViewDelegate> {
        UIAlertView *alertView;
    }
    
    @property (nonatomic, retain) UIAlertView *alertView;
    
    
    
    @implementation MyViewController
    
    @synthesize alertView;
    
    - (void)showAlert {
        if (alertView) {
            // if for some reason the code can trigger more alertviews before
            // the user has dismissed prior alerts, make sure we only let one of
            // them actually keep us as a delegate just to keep things simple
            // and manageable. if you really need to be able to issue multiple
            // alert views and be able to handle their delegate callbacks,
            // you'll have to keep them in an array!
            [alertView setDelegate:nil];
            self.alertView = nil;
        }
        self.alertView = [[[UIAlertView alloc]
                           initWithTitle:@"Error"
                           message:@"Something went wrong"
                           delegate:self
                           cancelButtonTitle:@"Cancel"
                           otherButtonTitles:@"Retry",nil] autorelease];
        [alertView show];
    }
    
    - (void)alertView:(UIAlertView *)alertViewParam didDismissWithButtonIndex:(NSInteger)buttonIndex {
        self.alertView = nil; // release it
        // do something...
    }
    
    - (void)dealloc {
        [alertView setDelegate:nil]; // this prevents the crash in the event that the alertview is still showing.
        self.alertView = nil; // release it
        [super dealloc];
    }
    

    这里的缺点是当用户关闭它时,您将无法处理警报视图的回调。但是,由于您的控制器已经消失/发布,因此您可能不需要这样做。如果这样做,则必须将警报视图的委托设置为将持续存在的内容。

    【讨论】:

    • 太棒了。效果很好。作为对您的建议的补充,我添加了 [alertView dismissWithClickedButtonIndex: 0 animated: YES];到 dealloc 的第一行,以便以编程方式删除 alertView。
    • 您好,当您在同一个视图控制器中显示警报时,上面的代码工作正常。但是当您转到另一个页面并返回此页面并再次尝试显示警报时,警报视图显示,但它的委托方法没有调用。它不会自动关闭。当单击取消按钮时,它会关闭。 [alertViewdismissWithClickedButtonIndex:0 动画:是];当您等待某些任务完成然后关闭时,此行不起作用。
    【解决方案5】:

    (人们可能会怀疑我回答这个问题迟了很多年,但它可能对其他人有所帮助)

    我猜你的问题出在弹出视图控制器的某个地方,你正在显示警报视图,同时试图将用户导航回视图。我建议你在这里遵循分层方法,即:

    首先将您的警报视图声明为全局对象,即

    @property(nonatomic,retain) UIAlertView *sampleAlert;
    

    现在在需要的地方编写您的警报视图显示代码,例如:

    -(IBAction)buttonClicked:(id)sender
    {
      self.sampleAlert = [[UIAlertView alloc] initWithTitle:@"test" message:@"test" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];
      [sampleAlert show];
      [sampleAlert release];
    }
    

    最后尝试在按下“确定”按钮时将用户导航到所需的视图,即您需要使用 alertView didDismissWithButtonIndex 方法,即

    - (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex 
    {
       if(alertView == sampleAlert)
       {
         [self.navigationController popViewControllerAnimated:YES];
       }
    }
    

    请注意,如果您有多个按钮的警报视图,您还需要检查按钮索引以区分操作,即检查使用

    if(alertView == sampleAlert && buttonIndex == 0)
    {
      //Do your stuff
    }
    else
    {
      //Do something else
    }
    

    这绝对可以避免应用程序崩溃,谢谢:)

    【讨论】:

      【解决方案6】:

      对我来说更简单的方法是将所有警报视图保存在一个数组中,当父视图被释放时,枚举 alertViews 数组并将委托分配给 nil。这将确保触摸事件被忽略并且应用程序将正常运行。

      // ARC world
      
      @property (strong, nonatomic) NSMutableArray *alertViews;
      
      - (void)dealloc
      {
          [self.alertViews makeObjectsPerformSelector:@selector(setDelegate:) withObject:nil];
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-05-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多