Ответ 1
ладно, ведь это заняло у меня больше времени, чем я ожидал, и я хотел бы изложить то, что я попробовал, и рассказать вам, какие у меня были с ним. Это, мы надеемся, спасет людей, пытающихся интегрировать Cocoa в существующее mainloop много времени в будущем. Первой функцией, которую я нашел при поиске обсуждаемого вопроса, была функция
nextEventMatchingMask:untilDate:inMode:dequeue:
но, как я уже сказал в этом вопросе, моя основная проблема заключалась в том, что мне пришлось бы постоянно проводить опрос о новых событиях, которые теряли бы время на некоторое время процессора. Поэтому я попробовал следующие два метода: просто позвольте моей функции обновления mainloops вызываться из главного сервера NSApplications:
-
Поместить пользовательское событие в NSApplication, перезаписать NSApplications
sendEvent:
и просто вызвать функцию обновления mainlops оттуда. Аналогично этому:NSEvent* event = [NSEvent otherEventWithType: NSApplicationDefined location: NSMakePoint(0,0) modifierFlags: 0 timestamp: 0.0 windowNumber: 0 context: nil subtype: 0 data1: 0 data2: 0]; [NSApp postEvent: event atStart: YES]; //the send event function of my overwritten NSApplication - (void)sendEvent:(NSEvent *)event { //this is my custom event that simply tells NSApplication //that my app needs an update if( [event type] == NSApplicationDefined) { myCppAppPtr->loopFunc(); //only iterates once } }
Это была только хорошая идея в теории, потому что, если мое приложение обновлено очень быстро (например, из-за быстрого срабатывания таймера), весь cocoa очередь событий перестала отвечать на запросы, потому что я добавил так многие пользовательские события. Поэтому не используйте это...
-
Используйте функцию performSelectorOnMainThread с функцией cocoa, которая в переверните вызовы моей функции обновления
[theAppNotifier performSelectorOnMainThread:@selector(runMyMainLoop) withObject:nil waitUntilDone:NO ];
Это было намного лучше, приложение и Cocoa EventLoop было очень отзывчивый. Если вы только пытаетесь добиться чего-то простого, я бы рекомендую спуститься по этому маршруту, так как это самый простой из них предлагаемый здесь. В любом случае у меня было очень мало контроля над порядком что происходит с этим подходом (это имеет решающее значение, если у вас есть многопоточное приложение), то есть когда мои таймеры выстрелили и сделают довольно долгое время, часто, когда они перепланировали перед любым новым мышь/клавиатура может быть добавлена в мой eventQueue и, следовательно, сделать весь ввод вялым. Turnin по вертикальной синхронизации в окне который был нарисован повторным таймером, было достаточно, чтобы это произошло.
-
В конце концов мне пришлось вернуться к
nextEventMatchingMask:untilDate:inMode:dequeue:
, и после некоторого проб и ошибок я действительно нашел способ заставить его работать без постоянного опроса. Структура моего цикла похожа на это:void MyApp::loopFunc() { pollEvents(); processEventQueue(); updateWindows(); idle(); }
где pollEvents и idle являются важными функциями, в основном я использую нечто похожее на это.
void MyApp::pollEvents() { NSEvent * event; do { event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate distantPast] inMode:NSDefaultRunLoopMode dequeue:YES]; //Convert the cocoa events to something useful here and add them to your own event queue [NSApp sendEvent: event]; } while(event != nil); }
Чтобы реализовать блокировку внутри функции idle(), я сделал это (не уверен, что это хорошо, но, похоже, отлично работает!):
void MyApp::idle() { m_bIsIdle = true; NSEvent * event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate distantFuture] inMode:NSDefaultRunLoopMode dequeue:NO]; m_bIsIdle = false; }
это приводит к тому, что Cocoa ждет, пока не будет событие, если это простое простаивание, просто завершается, и loopfunc снова запускается. Чтобы разбудить незанятую функцию, если один из моих таймеров (не использовать Cocoa таймеры), я снова использую настраиваемое событие:
void MyApp::wakeUp() { m_bIsIdle = false; //this makes sure we wake up cocoas run loop NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; NSEvent* event = [NSEvent otherEventWithType: NSApplicationDefined location: NSMakePoint(0,0) modifierFlags: 0 timestamp: 0.0 windowNumber: 0 context: nil subtype: 0 data1: 0 data2: 0]; [NSApp postEvent: event atStart: YES]; [pool release]; }
Поскольку я очищаю всю очередь событий Cocoa сразу же, у меня нет таких же проблем, как описано в разделе 1. Однако есть и некоторые недостатки такого подхода, потому что я думаю, что он не делает все, что
[NSApplication run]
делает внутренне, то есть приложение делегирует такие вещи:- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication*)theApplication { return YES; }
похоже, не работает, так или иначе, я могу жить с этим, так как вы можете легко проверить себя, если последнее окно просто закрыто.
Я знаю, что этот ответ довольно длинный, но так было и мое путешествие. Я надеюсь, что это поможет кому-то и не позволит людям совершать ошибки, которые я сделал.