【问题标题】:Run loop doesn't proceed events运行循环不进行事件
【发布时间】:2014-02-27 12:24:58
【问题描述】:

我正在使用标准(由 XCode 创建)应用程序中的运行循环进行测试。我的应用有 2 个按钮:

  1. Start Loop - 以某种模式启动 runloop(参见下面的代码);
  2. Stop Loop - 更改 self.stop 标志以停止运行循环。

`

- (IBAction)stopLoop:(id)sender
{
    self.stop = YES;
}

- (IBAction)startLoop:(id)sender
{
    self.stop = NO;
    do
    {
        [[NSRunLoop currentRunLoop] runMode:runLoopMode beforeDate:runLoopLimitDate];
        if (self.stop)
        {
            break;
        }
    } while (YES);
}

`

在哪里:
1.runLoopMode是预定义模式之一(我尝试了每个,默认,事件跟踪,模态,连接)。
2.runLoopLimitDate[NSDate distinctFuture],或[NSDate distinctPast],或关闭特征。
3、self.stop标志位安装在其他方法中,通过按钮调用。

就是这样,我的应用程序没有任何其他代码。

AFAIU,runloop 模式是一组事件源。因此,如果我在某种模式下运行 runloop,runloop 将继续那些与此模式相关联的事件源。
默认情况下,Cocoa 在默认模式下运行 runloop,所有事件都进行得很大。但是当用户按下startLoop 按钮时,我的应用程序冻结:

startLoop 方法永远不会打破这个无限循环。应用程序不会向我发送任何事件,因此 UI 冻结并且用户无法按下 stopLoop 按钮。如果我运行 Core Foundation 对应项,也会出现同样的问题。

但是,当我尝试通过 NSApplication(NSWindow 的)方法 nextEventMatchingMask:untilDate:inMode:dequeue: 接收事件并传递 same strong> 模式,我收到 UI 事件。

- (IBAction)startLoop:(id)sender
{
    self.stop = NO;
    do
    {
        NSEvent *event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:nil inMode:NSEventTrackingRunLoopMode dequeue:YES];
        if (event == nil)
        {
            break;
        }
        [NSApp sendEvent:event];
        if (self.stop)
        {
            break;
        }
    } while (YES);
}

有一个问题:“为什么如果我运行默认的运行循环模式或其他方式,我无法接收事件?”
谢谢你的建议。

【问题讨论】:

  • 你可能需要澄清你到底在做什么。例如,如果 other 方法 从不设置 self.stop,会发生什么情况?
  • 谢谢,我已经更新了我的问题。
  • 你只是这样阻塞runloop,限制日期意味着在此之前runloop被阻塞。您的循环正在立即重置并因此再次阻塞。停止按钮永远不会更改以发送其操作。你想达到什么目标?
  • 我正在探索运行循环。我希望,启动我的 runloop,我将能够继续事件(如简单的应用程序)但留在while 循环中,直到用户按下stopLoop 按钮。你能写更详细的解释吗?我不明白,为什么我运行的 runloop 不进行事件?我尝试向beforeDate: 发送不同的参数,但结果都相同。谢谢。
  • 尝试runUntilDate: 使用类似[NSDate dateWithTimeIntervalSinceNow:1.0]; 的日期,它不应该阻止事件处理。

标签: cocoa core-foundation nsrunloop


【解决方案1】:

您假设在 NSDefaultRunMode 中运行 NSRunLoop 会处理用户输入事件,例如按键或鼠标点击。我不这么认为。

NSApplication 从带有nextEventMatchingMask:untilDate:inMode:dequeue: 的事件中获取事件。通过像这样运行运行循环,您并没有真正获取事件队列的任何事件。

云你试试这样:

- (IBAction)startLoop:(id)sender
{
    do
    {
        NSEvent *event = [NSApp nextEventMatchingMask:NSAnyEventMask];
        [NSApp sendEvent:event];

        if (self.stop)
        {
            break;
        }
    } while (YES);
}

(还没有测试过)。

【讨论】:

  • 是的,谢谢,这种方法有效(也适用于其他模式,这是我的错误,我不会将此代码写入初始问题,我会的),但我对另一个很感兴趣:为什么运行循环模式不向我发送“UI”事件,而 nextEventMatchingMask:untilDate:inMode:dequeue: 具有相同的模式?
  • 单独运行 NSRunLoop 不会从应用程序的事件队列中获取任何事件(请参阅 Event Dispatch。您应该将 nextEventMatchingMask:untilDate:inMode:dequeue: 视为比 NSRunLoop 更高级别的 API。
【解决方案2】:

如果您将应用中的以下代码替换为 -startLoop: 方法,会发生什么情况?

- (NSString *) debugLogRunLoopInfo(BOOL didRun)
{
    NSLog (@"didRun? %@, runMode: %@, dateNow: %@, limitDate: %@",
        didRun ? @"YES" : @"NO",
        runLoopMode,
        [NSDate date],
        limitDate);
}

- (IBAction)startLoop:(id)sender
{
    BOOL didRun = NO;
    do
    {
        didRun = [[NSRunLoop currentRunLoop] runMode:runLoopMode beforeDate:runLoopLimitDate];
        [self debugLogRunLoopInfo:didRun];
        if (self.stop)
        {
            break;
        }
    } while (YES);
}

【讨论】:

  • didRun = YES;当前模式=无; limitDate = 2014-02-27 02:01:54 после полудня +0000(我在 16:03 运行我的应用程序)
  • 遵照 Volker 的上述建议:-runUntilDate: 会做你想做的事。
  • 我在上面建议的代码中修复了一个令人尴尬的错误。你可以再次尝试我的代码,但最终你会想要使用-runUntilDate: 方法而不是-runMode:beforeDate:
【解决方案3】:

-[NSRunLoop runUntilDate:] 方法在NSDefaultRunLoopMode 中旋转运行循环。由于您想尝试运行循环模式,您可以尝试下面的代码。

我已经实现了一个 -myRunLoopUntilDate:runMode: 方法,该方法执行 runUntilDate: 记录的操作,但允许您指定运行循环模式。

所有这些都是在我的文本编辑器中编译的(即根本没有编译),所以请谨慎购买。

- (NSString *) debugLogRunLoopInfo(BOOL didRun)
{
    NSLog (@"didRun? %@, runMode: %@, dateNow: %@, limitDate: %@",
        didRun ? @"YES" : @"NO",
        runLoopMode,
        [NSDate date],
        limitDate);
}

- (void) myRunLoopUntilDate:(NSDate *)limitDate runMode:(NSString *)runLoopMode
{
    BOOL didRun = NO;
    do {
        didRun = [[NSRunLoop currentRunLoop] runMode:runLoopMode beforeDate:limitDate];
        [self debugLogRunLoopInfo:didRun];
    } while (didRun && ([limitDate timeIntervalSinceNow] > 0));
}

- (IBAction)startLoop:(id)sender
{
    BOOL didRun = NO;
    do
    {
        [self myRunLoopUntilDate:runLimitDate runMode:runLoopMode];
        if (self.stop)
        {
            break;
        }
    } while (YES);
}

【讨论】:

    猜你喜欢
    • 2015-09-07
    • 2017-02-06
    • 2013-04-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-12
    相关资源
    最近更新 更多