【问题标题】:ObjC: ARC releasing dynamically created view controller too earlyObjC:ARC过早发布动态创建的视图控制器
【发布时间】:2013-07-14 17:13:14
【问题描述】:

我有一个名为 AllThingsViewController 的视图控制器,它动态创建名为 ThingViewController 的其他视图控制器,并将它们的顶级视图添加到 UIScrollView。 (我正在编写专有代码,因此我更改了类的名称,但我的代码结构完全相同。)

这是它的 loadView 方法包含的内容:

NSArray *things = [[ThingDataController shared] getThings];

if ([things count] == 0) {
    // code in this block is not relevant as it's not being executed...
} else {

    for(unsigned int i = 0; i < [things count]; ++i) {
        ThingViewController *thingViewController = [[ThingViewController alloc] init];
        [thingViewController loadView];

        [scrollView addSubview:thingViewController.topView];
        thingViewController.topView.frame = CGRectNewOrigin(thingViewController.topView.frame,
                0, thingViewController.topView.frame.size.height*i);

        [thingViewController displayThing:thing[i]];
    }
}

ThingViewController 的 loadView 方法如下所示:

- (void)loadView
{
    NSArray *topLevelObjs = nil;
    topLevelObjs = [[NSBundle mainBundle] loadNibNamed:@"ThingView" owner:self options:nil];
    if (topLevelObjs == nil)
    {
        NSLog(@"Error: Could not load ThingView xib\n");
        return;
    }
}

当我的应用程序启动时,一切都会正确显示,直到我尝试点击 ThingViewController 加载的 xib 中存在的按钮之一,此时它由于异常而崩溃:“无法识别的选择器已发送到实例”。似乎 ARC 过早地释放了我的 ThingViewController 实例。

查看我的代码,我认为这是因为它们没有被任何东西保留,所以我在我的 AllThingsViewController 类中创建了一个 NSMutableArray 作为实例变量,并开始向其中添加 ThingViewControllers:

NSArray *things = [[ThingDataController shared] getThings];

if ([things count] == 0) {
    // not being executed...
} else {

    for(unsigned int i = 0; i < [things count]; ++i) {
        ThingViewController *thingViewController = [[ThingViewController alloc] init];
        [thingViewController loadView];

        [scrollView addSubview:thingViewController.topView];
        thingViewController.topView.frame = CGRectNewOrigin(thingViewController.topView.frame,
                0, thingViewController.topView.frame.size.height*i);

        [thingViewController displayThing:thing[i]];

        [allThingsViewControllers addObject:thingViewController];
    }
}

然而,它并没有改变任何东西,即使这些对象被添加到数组中。最后,为了确认这是ARC提前发布的,我把“thingViewController”改成了AllThingsViewController中的一个实例变量,并改了:

ThingViewController *thingViewController = [[ThingViewController alloc] init];

成为:

thingViewController = [[ThingViewController alloc] init];

果然,当我点击它的按钮时,可滚动列表中的最后一项不会崩溃,但其他的会崩溃,因为它的 ThingViewController 没有被释放。

我对 ARC 还是比较陌生,但是在谷歌上搜索了一堆之后,我不知道如何解决这个问题。我该怎么办?

【问题讨论】:

  • 请显示完整“无法识别的选择器...”错误信息。
  • 无法识别的选择器错误通常并不表示发布过早。它抱怨的无法识别的选择器是什么?
  • 一个完全不同的对象,然后我的视图控制器在我点击按钮时试图响应选择器。我所做的研究表明这是因为视图控制器正在被释放,并且一个新对象现在占据了这部分内存。此外,每次我运行它都是尝试执行选择器的不同类型的对象。另外,如果我明确地将“thingViewController”设为实例变量(因此肯定有对它的强引用),它会停止至少一个视图控制器崩溃,所以我很确定问题是它也被释放了早。

标签: ios objective-c uiviewcontroller automatic-ref-counting


【解决方案1】:

几件事。

问题一:

这看起来像是你的错误的原因:

[allBillViewControllers addObject:billViewController];

应该是:

[allBillViewControllers addObject:thingViewController];

对吗?

问题 2

您没有正确地将视图控制器添加到视图层次结构中。应该是这样的:

[self addChildViewController:childViewController];
[childViewController.view setFrame:targetFrame];
[scrollView addSubview:childViewController.view];
[childViewController didMoveToParentViewController:self];

在移除子视图控制器时也是如此:

[childViewController willMoveToParentViewController:nil];
[childViewController.view removeFromSuperview];
[childViewController removeFromParentViewController];

问题 3

永远不要在视图控制器上显式调用loadView。每当您访问视图控制器的 view 属性时,UIKit 都会调用它。

问题 4

您必须将子视图控制器的 view 添加到滚动视图,而不是在其视图层次结构中添加任意子视图 topView。重构您的 ThingViewController 类以使您自己更简单。 :-)

【讨论】:

    【解决方案2】:

    让我们看看你的代码:

    for(unsigned int i = 0; i < [things count]; ++i) {
        ThingViewController *thingViewController = [[ThingViewController alloc] init];
        [thingViewController loadView];
    
        [scrollView addSubview:thingViewController.topView];
        thingViewController.topView.frame = CGRectNewOrigin(thingViewController.topView.frame,
                0, thingViewController.topView.frame.size.height*i);
    
        [billViewController displayThing:thing[i]];
    
        [allBillViewControllers addObject:billViewController];
    }
    

    在for循环的每个循环执行后,没有任何东西对ThingViewController有强引用。因此,它被释放和销毁。

    如果ThingViewControllerUIViewController 的子类,那么它应该成为滚动视图的视图控制器的“子视图控制器”。我推荐阅读the section on from the View Controller Programming Guide on creating custom container view controllers(即封装和显示其他视图控制器的视图控制器)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-06-08
      • 1970-01-01
      • 2015-04-19
      • 1970-01-01
      相关资源
      最近更新 更多