【问题标题】:Get a duplicated UIViewController instance after called performSegueWithIdentifier调用 performSegueWithIdentifier 后获取重复的 UIViewController 实例
【发布时间】:2014-12-18 15:04:33
【问题描述】:

********************** 更新:问题已解决!! 不要回复。谢谢! :)


我正在尝试为名为 RootViewController 的 UIViewController 实现多方向。它是应用程序中的根视图控制器。

我按照 Apple 教程 (https://developer.apple.com/library/ios/featuredarticles/ViewControllerPGforiPhoneOS/RespondingtoDeviceOrientationChanges/RespondingtoDeviceOrientationChanges.html) 中的说明在 RootViewController 类中实现以下代码:

- (void) viewDidLoad {
    ...
    // detect orientation changed
    [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(orientationChanged:)
                                                 name:UIDeviceOrientationDidChangeNotification
                                               object:[UIDevice currentDevice]];

    ....
}

- (void) orientationChanged:(NSNotification *)note {
    UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
    log(@"self: %@, orientation: %d", self, (int)orientation);

    UIDeviceOrientation deviceOrientation = [UIDevice currentDevice].orientation;
    if (UIDeviceOrientationIsLandscape(deviceOrientation) && !isShowingLandscapeView) {
        log(@"show landscape view");
        [self performSegueWithIdentifier:@"go_landscape" sender:self];
        isShowingLandscapeView = YES;
    } else if (UIDeviceOrientationIsPortrait(deviceOrientation) && isShowingLandscapeView) {
        log(@"show portrait view");
        [self dismissViewControllerAnimated:NO completion:nil];
        isShowingLandscapeView = NO;
    }
}

当我尝试将模拟器旋转到横向模式时,会调用orientationChanged 方法。但是当我将它旋转回来时,我看到了一个非常奇怪的情况:RootViewController 得到了一个重复的实例(原始实例 id = 0x7fc4eb813a00,而重复的实例在以下日志中为 0x7fc4eb081800)。

-[RootViewController orientationChanged:] [Line 796] self: <RootViewController:
0x7fc4eb813a00>, orientation: 1
-[RootViewController orientationChanged:] [Line 796] self: <RootViewController:   
0x7fc4eb813a00>, orientation: 3
-[RootViewController orientationChanged:] [Line 800] show landscape view
-[RootViewController orientationChanged:] [Line 796] self: <RootViewController: 
0x7fc4eb813a00>, orientation: 1
-[RootViewController orientationChanged:] [Line 804] show portrait view
-[RootViewController orientationChanged:] [Line 796] self: <RootViewController: 
0x7fc4eb081800>, orientation: 1

如果我再次将其旋转到横向模式,应用程序将崩溃,并显示以下日志。 (复制的没有segue。)

-[RootViewController orientationChanged:] [Line 796] self: <RootViewController: 
0x7fc4eb081800>, orientation: 3
-[RootViewController orientationChanged:] [Line 800] show landscape view
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', 
reason: 'Receiver (<RootViewController: 0x7fc4eb081800>) has no segue with 
identifier 'go_landscape''
*** First throw call stack:

故事板:

转场设置:

我真的不知道为什么会这样。我创建了一个新项目来测试方向更改的实现,不会发生奇怪的情况。有什么调查方向吗?

【问题讨论】:

  • 尚未阅读 Apple 的教程,但您的例外情况非常简单。在您的故事板中,您没有任何标识符为 go_landscape 的 segue,因此当您尝试执行该 segue 时,您的应用程序会崩溃。
  • 不,故事板确实有一个名为“go_landscape”的segue。原始实例确实成功地执行了它(注意日志的第二行)。所以日志显示“显示横向视图”并且没有发生崩溃。我不会犯这种愚蠢的错误。大声笑
  • 你能展示你的第二个视图控制器的方向改变实现吗?由于它发生在旋转回来,我不认为上面的代码是崩溃的原因。
  • 同一个视图控制器——RootViewController。如果我在orientationChanged 方法中标记代码。这样应用就不会再崩溃了,也不会创建重复的 RootViewController 实例。
  • 它可能是同一个视图控制器的 class,但不是同一个 instance,因为您正在“进入”它。

标签: ios objective-c iphone uiviewcontroller segue


【解决方案1】:

您发布的代码旨在在您切换到横向时创建一个新的视图控制器。 performSegue 方法创建一个新的视图控制器。这就是它的工作原理。

我总是尽量避免仅仅为了支持不同的方向而创建新的视图控制器。相反,我设置了代码以适应几何变化。与使用 AutoLayout 相比,我更擅长使用旧样式调整大小的蒙版,但我已经使用了这两种方法。

【讨论】:

    【解决方案2】:

    哦,我的... performSegueWithIdentifier 方法确实创建了另一个实例,即使它是同一个类。我找到了应用程序崩溃的根本原因。

    在我的新项目中,当方向改变时,日志显示如下。当调用dismissViewControllerAnimated方法解除self时,新创建的实例会被dealloc。

    self: <RootViewController: 0x7f99d1518890>, orientation: 3, isShowingLandscapeView: 0
    show lanscape view
    self: <RootViewController: 0x7f99d1518890>, orientation: 1, isShowingLandscapeView: 1
    show portrait view
    self: <RootViewController: 0x7f99d1727500>, orientation: 1, isShowingLandscapeView: 0
    [RootViewController dealloc] [Line 35] !!!!!! dealloc instance: <RootViewController: 0x7f99d1727500>
    

    但是,我原来的项目中的日志并没有像上面那样显示......新创建的实例不是dealloc!所以它会崩溃,因为第二个实例是基于第二个故事 UIViewController 布局创建的,并且它不包含 segue“go_landscape”。

    self: <RootViewController: 0x7fc142818400>, orientation: 3, isShowingLandscapeView: 0
    show landscape view
    self: <RootViewController: 0x7fc142818400>, orientation: 1, isShowingLandscapeView: 1
    show portrait view, self: <RootViewController: 0x7fc142818400>
    self: <RootViewController: 0x7fc142200600>, orientation: 1, isShowingLandscapeView: 0
    self: <RootViewController: 0x7fc142818400>, orientation: 3, isShowingLandscapeView: 0
    show landscape view
    self: <RootViewController: 0x7fc142200600>, orientation: 3, isShowingLandscapeView: 0
    show landscape view
    *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Receiver (<RootViewController: 0x7fc142200600>) has no segue with identifier 'go_landscape''
    

    但是为什么!???这两个项目都使用 ARC 机制并处理相同的代码。为什么新项目中的新实例会正常dealloc,而原项目中的新实例不会dealloc!???????

    必须解决这个问题,否则在方向改变时会导致内存泄漏,因为它总是创建一个新的垃圾实例。

    【讨论】:

      【解决方案3】:

      天啊…………我知道为什么原来项目中的新实例不会被dealloc了。因为它会创建一个 NSTimer 对象并继续运行。所以实例永远不会被释放。好吧,是我的错。 ~"~

      - (void) startTimer {
          timer = [NSTimer scheduledTimerWithTimeInterval:1.0f
                                                   target:self
                                                 selector:@selector(tick)
                                                 userInfo:nil
                                                  repeats:YES];
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-04-21
        • 1970-01-01
        • 1970-01-01
        • 2016-08-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-08-03
        相关资源
        最近更新 更多