弹出窗口关闭,出现[UIPopoverController dealloc],但弹出窗口仍可见

6

我在我的视图控制器中存储了一个UIPopoverController类的强引用属性。当用户旋转iPad时,如果弹出框可见,我会关闭它并将该属性设置为nil。

if (self.popover != nil) {
    [self.popover dismissPopoverAnimated:NO];
    self.popover.delegate = nil;
    self.popover = nil;
}

当代码执行到self.popover = nil时,ARC会尝试释放UIPopoverController,但由于它仍然可见,因此会导致崩溃。
我该如何在不崩溃的情况下关闭并释放popover?

2
问题解释得很好。我正在经历完全相同的情况。 - funroll
3个回答

16

首先,建议检查浮层是否正在显示,这也会方便地检查它是否已分配:

if ([self.popover isPopoverVisible]) {
    [self.popover dismissPopoverAnimated:NO];
}

现在的问题是,如果像这样以编程方式取消弹出窗口,你将无法收到委托回调- (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController,但你需要一个强引用来保留弹出窗口,直到它不再可见。

要解决此问题,可以延迟将属性设置为nil,直到返回主运行循环,因为当返回主运行循环时,所有动画都已完成,因此弹出窗口将不再可见。

你需要将设置弹出窗口为nil的代码移到另一个方法中:

- (void)releasePopover {
    self.popover.delegate = nil;
    self.popover = nil;
}

然后,在您的旋转回调中,添加此方法以在主运行循环中触发。我喜欢通过将一个调用操作添加到主运行循环来实现这一点:

if ([self.popover isPopoverVisible]){
    [self.popover dismissPopoverAnimated:NO];
    NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(releasePopover) object:nil];
    [[NSOperationQueue mainQueue] addOperation:invocationOperation];
}

最后,出于清洁考虑,您可能希望从内部调用-releasePopover方法在您的- (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController回调中。

因此,将它们组合在一起:

- (void)releasePopover
{
    self.popover.delegate = nil;
    self.popover = nil;
}

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
    if ([self.popover isPopoverVisible]){
        [self.popover dismissPopoverAnimated:NO];
        NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(releasePopover) object:nil];
        [[NSOperationQueue mainQueue] addOperation:invocationOperation];
    }
}

- (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController
{
    [self releasePopover];
}

话虽如此,除非有充分的理由,否则您可能只想保留弹出窗口以便重用,并且仅在收到低内存警告和/或如果视图未加载时将其设置为nil,正如Chris Loonam的答案所提到的那样。


1

如果你真的觉得有必要,可以在viewDidUnload中将其设置为nil。由于ARC会自动释放对象,我不确定这样做是否真的有必要。


视图并没有被卸载,我只是遇到了方向改变的情况。 - Kenny Wyland
这是因为我想在被关闭后将其从内存中移除,我还会检查该变量以查看是否已经存储了一个等价值等。 - Kenny Wyland

1
站在Simon的答案基础上,这是我修复崩溃的方法:
// set to nil on main queue to prevent "dealloc'd while still visible" exception
dispatch_async(dispatch_get_main_queue(), ^{
        self.popover = nil;
});

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