1

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

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

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

我使用主线程是因为在通知线程上进行处理可能会在控制器准备好之前释放控制器。

现在,这工作得很好——除非当前有动画师正在运行。我在某些地方使用动画师,通过NSAnimationContext. 我看过这个 QA,答案是不可接受的。等一会,只是为了做动画,真的是粗制滥造,不保证能干;确实没有。我尝试使用performSelector:withObject:afterDelay,即使延迟大于当前动画持续时间,它仍然会导致动画师针对 nil 对象运行。

像这样进行控制器清理的首选方法是什么?不使用NSAnimationContext而是使用NSAnimation,哪个有stopAnimation方法?

4

2 回答 2

2

我没有使用过NSAnimationContext(总是使用NSAnimation,但主要是出于历史原因)。但我喜欢管理与此类似的事情的典型方式是创建短暂的保留循环。

马克的回答完全是正确的想法,但不需要投票。您在完成处理程序中引用的事实self意味着self在完成处理程序运行之前无法解除分配。你是否读过书实际上并不重要animating。ARC 必须让你一直待在完成块运行之前,因为该块引用了你。

另一种类似的技术是使用objc_setAssociatedObject. 这将保留您,直到完成块运行。在完成块中,self作为关联对象删除,然后您就可以自由地解除分配。这种方法的好处是它不需要像animating.

当然,有时合适的最后的、绝望的措施是创造短暂的自我参照。例如:

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

我不提倡最后一种选择。但很高兴知道它存在,更重要的是知道它为什么起作用。

于 2013-02-17T23:29:57.490 回答
2

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

但是对于视图上的隐式动画之类的东西,您可以简单地使用完成方法。

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

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

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

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

于 2013-02-17T19:30:49.207 回答