可能正在发生动画时取消分配窗口

时间:2013-02-17 19:21:46

标签: objective-c cocoa appkit nswindowcontroller

我的AppDelegate维护一个活动窗口控制器列表,以避免ARC过早地解除分配它们。所以我有一个这样的通知处理程序:

- (void) windowWillClose: (NSNotification*) notification {
    [self performSelectorOnMainThread: @selector(removeWindowControllerInMainThread:)
        withObject: windowController
        waitUntilDone: NO];
}

- (void) removeWindowControllerInMainThread: (id) windowController {
    [windowControllers removeObject: windowController];
}

我使用主线程,因为在通知线程上进行处理可能会在控制器准备就绪之前解除分配。

现在,这非常有效 - 除非当前有动画师正在运行。我通过NSAnimationContext在某些地方使用动画师。我看过this QA,答案是不可接受的。等待一段时间,只是为了完成动画,真是粗制滥造,并不能保证工作;事实上它没有。我尝试使用performSelector:withObject:afterDelay,即使延迟时间大于当前动画持续时间,它仍会导致动画师针对nil对象运行。

这样做控制器清理的首选方法是什么?不是使用NSAnimationContext而是使用NSAnimation而是使用stopAnimation方法?

2 个答案:

答案 0 :(得分:2)

首先,如果你的某些动画无限期地运行 - 或者很长一段时间 - 你将不得不有办法阻止它们。

但是对于像视图上的隐式动画这样的东西,你可以简单地使用一个完成方法。

  self.animating=YES;
  [NSAnimationContext runAnimationGroup:^(NSAnimationContext *context){
    [[v animator] setAlphaValue: 1];
} completionHandler:^{
    self.animating=NO;
}];

现在,您只需要轮询您的动画是否正在运行,如果它没有运行,请继续关闭窗口。

进行轮询的一个好方法是设置一个具有固定延迟的计时器。如果动画仍在运行,只需重置计时器并等待另一个时间间隔。

或者,您可以从完成处理程序发送通知。

答案 1 :(得分:2)

我没有使用NSAnimationContext(总是使用NSAnimation执行此操作,但主要是出于历史原因)。但我喜欢管理类似于此的东西的典型方法是创建短暂的保留循环。

Mark的回答恰恰是正确的想法,但不需要轮询。您在完成处理程序中引用self这一事实意味着self在完成处理程序运行之前无法解除分配。你是否读过animating并不重要。 ARC必须让你一直运行,直到完成块运行,因为块引用了你。

另一种类似的技术是使用objc_setAssociatedObject将自己附加到动画上下文。这将保留您直到完成块运行。在完成块中,删除self作为关联对象,然后您可以自由解除分配。这种方法的好处是它不需要像animating这样的虚假额外属性。

当然,偶尔适当的最终绝望措施是创造短暂的自我参照。例如:

- (void)setImmortal:(BOOL)imortal {
  if (immortal) {
    _immortalReference = self;
  }
  else {
    _immortalReference = nil;
  }
}

我不是在提倡最后一个选择。但是知道它存在是很好的,更重要的是要知道它的工作原理。