【问题标题】:NSViewController -- dismiss with memory cleanupNSViewController -- 清除内存
【发布时间】:2016-06-06 22:54:32
【问题描述】:

我的环境是 Yosemite 10.10.5 和 Xcode 7.2 使用 ARC。

在一个简单的测试程序中,我尝试了多种方法来关闭 NSViewController,但它们都显示出内存处理问题。

在我的主视图控制器中,我有以下代码。 (通知片段用于测试解除呈现控制器的各种方法。)

- (IBAction)showFirstReplacement:(id)sender {
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(dismissWithNotification:) name:@"removeFirst" object:nil];
    NSStoryboard *sb = [self storyboard];
    FirstReplacement *controller = [sb instantiateControllerWithIdentifier:@"first_replacement"];
    [self presentViewControllerAsSheet:controller];
}

- (void)dismissWithNotification:(NSNotification *)notification {
    NSViewController *controller = [notification object];
    [self dismissViewController:controller];
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

FirstReplacement里面,我有:

- (IBAction)dismiss:(id)sender {
    [self dismissViewController:self];
//  [[NSNotificationCenter defaultCenter] postNotificationName:@"removeFirst" object:self];
//  [[self presentingViewController] dismissViewController:self];
}

取消注释此方法中的三行中的任何一行都会产生正确的视觉结果,但是.... 根据我在 dismiss: 中启用的调用,我在分析时会得到不同的结果。使用self dismissViewController:,我看不到任何泄漏,但FirstReplacement 对象没有被释放。使用其他两种方法中的任何一种都可以消除已关闭的 FirstReplacement,但每次关闭视图控制器时都会泄漏一个 16 字节的 malloc 块和一个 NSMutableArray

根据 Instruments 的说法,泄漏与称为 [NSViewController _addPresentedViewController:] 的方法有关。

是否有其他必要的清理步骤来防止这些泄漏(或非泄漏情况下的内存膨胀)?

【问题讨论】:

  • FirstReplacement::dismiss 是从哪里调用的?
  • 连接到情节提要中的 IBAction 的按钮。
  • 我能够使用 Swift 和故事板在 10.11.6 上重现此错误。但是,从 10.13.2 开始,该错误似乎已修复。在 10.13.2 上,我在 Xcode 的 Memory Graph Debugger(在运行时问题下)或在 Instruments 应用程序中使用 Leaks Instrument 时没有看到泄漏。

标签: objective-c macos memory-leaks nsviewcontroller


【解决方案1】:

呈现另一个视图控制器的视图控制器也负责解除它。所以FirstReplacement 的dismiss 方法中没有一行是正确的。相反,您应该在 FirstReplacement 中创建一个委托,以便它可以通知其委托(主视图控制器)它应该被解除。

FirstReplacement.h

@class FirstReplacement;

@protocol FirstReplacementDelegate <NSObject>
- (void)firstReplacementShouldDismiss:(FirstReplacement *)controller;
@end

@interface FirstReplacement : NSViewController
@property (nonatomic, weak) id<FirstReplacementDelegate> delegate;
@end

FirstReplacement.m

- (IBAction)dismiss:(id)sender {
    [self.delegate firstReplacementShouldDismiss:self];
}

然后在您的主视图控制器中:

- (IBAction)showFirstReplacement:(id)sender {
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(dismissWithNotification:) name:@"removeFirst" object:nil];
    NSStoryboard *sb = [self storyboard];
    FirstReplacement *controller = [sb instantiateControllerWithIdentifier:@"first_replacement"];
    controller.delegate = self;
    [self presentViewControllerAsSheet:controller];
}

- (void)firstReplacementShouldDismiss:(FirstReplacement *)controller {
    [self dismissViewController:controller];
}

虽然发布通知似乎与委托相同,但事实并非如此。不同之处在于,当dismissWithNotification 触发时,您仍在执行FirstReplacement::dismiss 中的代码。 NSNotificationCenter::postNotificationName 直到所有的观察者都完成了他们的选择器才完成执行。因此,即使解除代码在主视图控制器中执行,它仍然从解除方法运行。

如果您仍然不相信,请覆盖 FirstReplacement::dealloc 以打印日志语句。您将看到 dealloc 不是使用您的任何方法调用的,而是使用委托调用的。

【讨论】:

  • 这基本上就是我在通知示例中所做的,告诉主控制器在单击按钮时关闭。此外,如果我启用该行,[self presentingViewController] 是主要的。
  • 我在FirstReplacement 中完成了你的“覆盖释放”测试。它被称为通知方法,presentingViewController 一种和委托方法。唯一没有调用 dealloc 的是 self 调用(这显然不起作用)。更重要的是,调用 dealloc 的所有三个方法都泄漏了相同的两个项目。
  • 嗯,当我测试它时,它并没有被我调用。如果使用委托仍然会泄漏内存,那么我就没有主意了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-12-20
  • 2014-01-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多