我的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
方法?
答案 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;
}
}
我不是在提倡最后一个选择。但是知道它存在是很好的,更重要的是要知道它的工作原理。