NSTimer - настройка простой ванили не срабатывает

Компиляция в XCode 3.1.1 для целевой OSX 10.5.8, 32-разрядной версии и сборки i386.

У меня есть модальный цикл выполнения, работающий в NSWindow wloop и NSView vloop. Модальный цикл запускается первым. Он запускается, работает и останавливается, как и ожидалось. Вот начало:

[NSApp runModalForWindow: wloop];

Затем, когда пользователь нажимает левую кнопку мыши, я делаю следующее:

if (ticking == 0) // ticking is set to zero in its definition, so starts that way
{
    ticking = 1; // don't want to do this more than once per loop
    tickCounter = 0;
    cuckCoo = [NSTimer scheduledTimerWithTimeInterval: 1.0f / 10.0f         // 10x per second
                                               target: self                 // method is here in masterView
                                             selector: @selector(onTick:)   // method
                                             userInfo: nil                  // not used
                                              repeats: YES];                // should repeat
}

Проверяя возврат вызова, я получаю объект таймера и могу подтвердить, что вызов таймера сделан, когда я этого ожидаю.

Теперь, согласно документации, результирующий NSTimer, хранящийся глобально как cuckCoo, должен быть автоматически добавлен в текущий цикл выполнения. Текущий цикл выполнения определенно является модальным - в это время другие окна заблокированы, и только окно с предполагаемым действием мыши принимает сообщения.

Вызываемый при этом метод onTick очень прост (потому что я не могу заставить его активироваться), он находится в коде vloop NSView, в котором все это происходит:

- (void) onTick:(NSTimer*)theTimer
{
    tickCounter += 1;
    NSLog(@"Timer started");
}

Затем, когда пришло время остановить модальный цикл (который, кстати, работает нормально), я делаю следующее:

[cuckCoo invalidate];
[NSApp stop: nil];
ticking=0;
cuckCoo = NULL;
NSLog(@"tickCounter=%ld",tickCounter);

ticking и tickCounter - это глобальные длинные позиции.

Я не получаю сообщение NSLog из onTick, а значение tickCounter остается равным нулю, как сообщает NSLog в конце цикла выполнения.

Все это компилируется и работает нормально. У меня просто никогда не бывает клещей. Я в полной растерянности. Есть идеи, кто-нибудь?


person fyngyrz    schedule 22.03.2011    source источник


Ответы (2)


Проблема связана с этим утверждением: «Текущий цикл выполнения определенно является модальным». В Какао каждый поток имеет не более одного цикла выполнения, и каждый цикл выполнения может выполняться в различных «режимах». Типичными режимами являются режимы по умолчанию, отслеживание событий и модальный. По умолчанию используется режим, в котором обычно выполняется цикл, в то время как отслеживание событий обычно используется для отслеживания сеанса перетаскивания мыши, а модальный режим используется для таких вещей, как модальные панели.

Когда вы вызываете - [NSTimer scheduleTimerWithTimeInterval: target: selector: userInfo: Repeats:], он сразу же планирует таймер, но только планирует его для режима цикла выполнения по умолчанию, а не для модального режима цикла выполнения. Идея заключается в том, что приложение, как правило, не должно продолжать работать за модальной панелью.

Чтобы создать таймер, который срабатывает во время модального цикла выполнения, вы можете использовать - [NSTimer initWithFireDate: interval: target: selector: userInfo: Repeats:], а затем - [NSRunLoop addTimer: forMode:].

person Jon Hess    schedule 22.03.2011
comment
Ух ты. Спасибо за ответ. Но что за бардак. Итак, я получаю текущий цикл выполнения, получаю из него текущий режим выполнения (я понятия не имею, что это такое, лучше спросите), получаю дату (сейчас), создаю экземпляр времени с помощью метода класса NSTimer timerWithTimeInterval: target: selector: userInfo: повторяет: измените его с помощью метода объекта -timer initWithFireDate: interval: target: selector: userInfo: Repeats :, затем currentRunLoop addTimer: forMode:, это все? Я очень надеюсь, что нет, потому что он дает сбой в addTimer, а он ужасающе непрямой и сложный. Я просто хочу создать таймер, который тикает. Вроде ... вроде должно быть проще. - person fyngyrz; 22.03.2011
comment
Ладно, я понял. Предложение Джона вообще не работает. Но используя JUST + NSTimer timerWithTimeInterval: target: selector: userInfo: repeat: does, if я не использую возврат из - NSRunLoop currentMode, а вместо этого заполняю поле addTimer: timer forMode: с помощью NSModalPanelRunLoopMode. Это также избавляет от необходимости прыгать через обруч с датой. И я должен указать, что эту эзотерическую последовательность нелегко найти, если предположить, что она вообще задокументирована, поэтому, надеюсь, Google примет это к сведению. - person fyngyrz; 22.03.2011
comment
Разве вы не сделали именно то, что я предлагал? Вы создали таймер, а затем добавили его в нужном режиме. - person Jon Hess; 23.03.2011
comment
Только частично. Вы сказали, что используйте - [NSTimer initWithFireDate: interval: target: selector: userInfo: repeat:], метод объекта, требующий сначала выделения таймера, а затем применение метода объекта, а также лишнюю дату. Я пробовал это. Оно сломалось. Поэтому вместо этого я использовал метод класса, как описано, так что таймер был создан в правильном режиме, и это сработало. У вас была правильная основная идея, хорошо - модальный цикл выполнения не работает с обычными запросами для таймеров - но неправильный уровень метода для его подхода (объект вместо класса). Тем не менее, хороший указатель! - person fyngyrz; 23.03.2011
comment
Думаю, я указал на вашу проблему - наличие режимов цикла выполнения. Вы должны были взять эти знания и применить их к тому, как именно работает ваше приложение. То, что вы предпочли метод, использующий интервал вместо даты, не имеет отношения к правильности ответа. - person Jon Hess; 23.03.2011
comment
Нет, Джон, проблема заключалась в том, что то, что ты предлагал, не удалось. Все остальные достоинства в сторону. Ваш подход просто не сработал. То, что я опубликовал, работает. Вот почему я разместил его - потому что эта часть вашего ответа ведет по неправильному пути, хотя и по правильным причинам - runloopmodes. Что касается того, что мне нужно было сделать, я сделал это и вернулся, чтобы поделиться преимуществами, поэтому я не вижу актуальности. - person fyngyrz; 26.03.2011

Ответ конкретно на ...

[NSApp runModalForWindow: wloop];

... после входа в модальный цикл выполнения:

NSRunLoop *crl;

    cuckCoo = [NSTimer timerWithTimeInterval: 1.0 / 10
                                      target: self
                                    selector: @selector(onTick:)
                                    userInfo: nil
                                     repeats:YES];
    crl = [NSRunLoop currentRunLoop];
    [crl addTimer: cuckCoo forMode: NSModalPanelRunLoopMode];

(crl получен отдельно для ясности) Где метод onTick имеет вид:

- (void) onTick:(NSTimer*)theTimer
{
    // do something tick-tocky
}
person fyngyrz    schedule 22.03.2011