【问题标题】:How to ensure NSTimer always runs in NSView?如何确保 NSTimer 始终在 NSView 中运行?
【发布时间】:2011-01-11 05:25:15
【问题描述】:

我在自定义NSView 中有一个NSTimer。计时器设置为始终以特定间隔重复,一旦触发,计时器应该更新一堆视图参数。

这一切都很好,但是当视图失焦时(即右键单击工具栏以拉出菜单),计时器会延迟并且在视图失焦期间不会触发。

我的问题是,即使视图失焦,如何确保计时器始终触发并更新视图参数?

【问题讨论】:

    标签: cocoa nsview nstimer


    【解决方案1】:

    当您执行某些事件(例如显示上下文菜单)时,运行循环将进入事件跟踪模式。这意味着只会处理某些事件。要在运行循环处于此模式时触发计时器,请将 NSEventTrackingRunLoopMode 包含在计时器的模式列表中。

    NSTimer *timer = [NSTimer timerWithTimeInterval:theInterval target:theTarget selector:theSelector userInfo:theUserInfo repeats:shouldRepeat];
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSEventTrackingRunLoopMode];
    

    【讨论】:

    • 这几乎是我的解决方案。我实际上创建了一个 NSOperation 来保持我的计时器同时运行到主线程。
    • EXC_BAD_ACCESS 使用时。
    【解决方案2】:

    一种可能的解释:

    计时器触发,事件按时添加到队列中。但是,大部分 AppKit 都不是线程安全的,所以......直到运行循环的下一次迭代,您才能看到更新。 iow,事件被阻塞了,因为这些事件都是通过主运行循环/线程进行管道传输的。

    有意义吗?

    注意:为了使问题更加复杂,异步绘图对于 os x(在 NSView 级别)来说是相当新的,因此请确保您测试您打算支持的所有主要版本的操作系统。结果/行为会有所不同。

    【讨论】:

    • 有时您必须下拉到较低级别的图形库和/或在自定义视图中使用异步绘图来克服这个问题。
    • 我明白了。我只支持最新版本的 OSX。那么你会建议分拆另一个线程并在单独的线程上绘制/更新视图吗?
    • 嗯,这是我最初的假设——在你的情况下,这实际上可能不是问题。在重写您的 ui 以在 mt 上下文中正常运行之前,值得证明或反驳。如果这一点得到证实,并且您绝对必须拥有该功能,那么是的,这就是我要采取的路线。您将覆盖 NSView 中的 10.6 异步方法。当然,所有子视图、叠加层和超级视图(如果不是不透明的)也必须能够在此上下文中呈现。否则,它会因为其他原因看起来很糟糕(假设框架允许这样做,这是可能的)。 mt 绘图会很快变得复杂(续)
    • 如果您有很多异步绘图要做并且习惯使用 CG,那么使用 CoreGraphics 实现复杂视图是一种替代方法。 AppKit 的大部分内容是 CG 框架上的 objc 包装器。然而,像这样的复杂系统可能意味着您需要重写大量 NSView 的功能,具体取决于您的需要。 OpenGL 是另一种选择,但 CG 与 NSViews 配合得更好,因此它是在 osx 上进行 2d 渲染的一个很好的中间道路。
    • 我通过将 NSOperation 子类化并使其与主循环同时运行来纠正了该问题。 NSOperation 对象包含我所有的计时器。当计时器触发时,它会告诉主线程中的视图进行更新。挺容易。感谢您的帮助。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-11-05
    • 1970-01-01
    • 2015-06-12
    • 2021-01-24
    • 2017-05-29
    • 2013-05-09
    • 2023-02-26
    相关资源
    最近更新 更多