Как я могу удалить UIApplicationMain из приложения iPhone? - PullRequest
11 голосов
/ 03 февраля 2010

Я пытаюсь перенести библиотеку игр на iPhone. В отличие от SDL, эта библиотека не полностью контролирует вашу функцию main (), с ней связываются быстро возвращаемые функции из вашего собственного кода. Так, например, очевидный псевдокод:

int main() {
  library_init();
  // game init code here
  while(we_have_not_quit_the_game) {
    library_message_loop();
    library_init_render();
    // render stuff
    library_end_render();
    // update game state
  }
  library_shutdown();
}

iPhone усложняет эту задачу, поскольку требует вызова функции UIApplicationMain, которая никогда не возвращается. Я просто не могу вернуться к пользовательскому коду после library_init ();.

Я не уверен, что это необходимо - есть NSRunLoop, который предположительно можно использовать для обработки событий. Однако я не знаю, делает ли UIApplicationMain что-то еще важное. (Обратите внимание, что у меня нет планов использовать файлы .nib, и это единственное, что я обнаружил в UIApplicationMain.)

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

  • В Init создайте новый поток, запустите UIApplicationMain в этом потоке. Либо сообщайте все события через потоки (тьфу), либо просто переведите поток UIApplicationMain в спящий режим и используйте CFRunLoop в основном потоке. Однако я слышал, что UIApplicationMain не нравится работать в другом потоке.
  • Полностью игнорировать UIApplicationMain, просто используйте NSRunLoop. Я пропущу важные настройки iPhone? Кто знает!
  • Сделайте что-то ужасное с longjmp (), чтобы выпрыгнуть из кода UIApplicationMain после установки, молитесь, чтобы он не делал ничего важного во время демонтажа.

Предложения

Ответы [ 4 ]

5 голосов
/ 06 февраля 2010

Похоже, я отвечаю на свой вопрос здесь!Я не принимаю мой ответ, пока не смогу одновременно протестировать его на реальном оборудовании и загрузить в магазин приложений.Тем не менее, я буду хранить здесь свою самую актуальную информацию, в том числе о том, какие опции не работали.

Идея # 1 : Оказывается, что каждый NSRunLoop является поточнымконкретный.Если я создаю UIApplicationMain в отдельном потоке, он не получает никаких сообщений.В качестве побочного эффекта это делает невозможным определение того, когда он завершает инициализацию, поэтому, если он выполняет какие-либо функции, не обеспечивающие безопасность потоков, он просто не будет работать.Возможно, я смогу отправить ему сообщение через потоки, чтобы выяснить, когда он завершит инициализацию, но сейчас я называю это тупиком.

Идея # 2 : UIApplicationMain выполняетмного тонких вещей.Я не уверен, чем это ограничено, но я не смог заставить что-либо работать без участия UIApplicationMain.Идея № 2 верна.

Идея № 3 : важно принимать сигналы ОС - вам нужно знать, есть ли наложение телефонных звонков или вы собираетесь выйти,Вдобавок ко всему, некоторые из сообщений настройки кажутся жизненно важными для правильного запуска приложения.Мне не удалось найти какой-либо метод, позволяющий сохранить отправку сообщений, не находясь внутри UIApplicationMain.Единственные варианты, которые я придумал, были NSRunLoop и CFRunLoop.Ни один из них не работал - сообщения не приходили так, как я хотел.Возможно, я не пользуюсь этим правом, но в любом случае Идея № 3 отсутствует.

Совершенно новая сумасшедшая Идея № 4 : можно использовать setjmp / longjmp для подделки сопрограмм вC / C ++.Хитрость заключается в том, чтобы сначала установить указатель стека на какое-то значение, которое не будет мешать чему-либо важному, затем запустите вторую процедуру, а затем прыгайте назад и вперед, делая вид, что у вас два стека.Все становится немного грязно, если ваша «вторая сопрограмма» решает вернуться из своей основной функции, но, к счастью, UIApplicationMain никогда не возвращается, так что это не проблема.

Я не знаю, есть ли способявно установить указатель стека на реальном оборудовании, скажем, на часть данных, которые я выделил на лету.К счастью, это не имеет значения.По умолчанию iPhone имеет 1 МБ стека, что достаточно для размещения нескольких сопрограмм.

В настоящее время я использую alloca (), чтобы переместить указатель стека на 768 килобайт, а затем порождает UIApplicationMain.затем с помощью setjmp / longjmp отскочить назад и вперед между моей «подпрограммой пользовательского интерфейса» и моей «основной подпрограммой».Пока что это работает.

Предостережения:

  • Невозможно узнать, когда в «подпрограмме пользовательского интерфейса» нет сообщений для обработки и когда нет сообщений дляобрабатывать, он будет просто блокировать на неопределенный срок, пока это не так.Я решаю это путем создания таймера, который срабатывает каждые 0,1 миллисекунды.Каждый раз, когда срабатывает таймер, я выпадаю из своей «основной процедуры», выполняю одиночный игровой цикл, а затем возвращаюсь в «процедуру пользовательского интерфейса» для следующего такта таймера.Чтение документации показывает, что она не будет накапливать «вызовы по таймеру» бесконечно.Мне кажется, что я получаю сообщение «прекратить» надлежащим образом, хотя мне еще не удалось тщательно протестировать его, и я не проверял никаких других важных сообщений.(К счастью, всего четыре сообщения, и одно из них связано с настройкой.)

  • Большинство современных ОС не выделяют весь стек сразу. IPhone, вероятно, один из них. Чего я не знаю, так это того, что увеличение указателя стека на 3/4 мегабайта вперед выделит все, так сказать, «за ним». Если это так, я могу эффективно тратить 3/4 мегабайта оперативной памяти, что на iPhone очень важно. Эту проблему можно решить, повернув указатель вперед на меньшую величину, но на самом деле это катастрофическое изменение размера стека: оно эффективно ограничивает ваш стек тем, насколько далеко вы поднимаете указатель вперед, и вам придется это выяснить заранее. Некоторые дозорные данные в стеке в сочетании с хорошим мониторингом и системой регистрации проблем размера стека, возможно, могут решить эту проблему, но это нетривиальная проблема. (В качестве альтернативы, если я могу выяснить, как перебирать указатель стека непосредственно на собственном оборудовании, я могу просто выполнить malloc () / new [] несколько килобайт, указать на него указатель стека и использовать его в качестве моего нового стека. Я должен выяснить, сколько места ему нужно, но я сомневаюсь, что это будет много, учитывая, что это не очень много.)

  • Это в настоящее время не проверено на реальном оборудовании (дайте мне неделю или две, у меня есть другой проект, чтобы закончить первым.)

  • Я понятия не имею, выяснит ли Apple, что я делаю, и наклею на нее гигантскую ОТКЛОНЕННУЮ наклейку, когда я попытаюсь отправить ее в магазин приложений. Это, скажем так, немного за пределами их намерений по API. Скрестив пальцы.

Я буду обновлять этот пост и официально принимать его, как только проверим, что он работает.

Позднее обновление: меня отвлекло множество других вещей. С тех пор у меня было несколько изменений, которые сделали меня гораздо менее заинтересованным в разработке Apple Мой нынешний подход не показал никаких признаков того, что не работает, но у меня действительно нет мотивации, чтобы продолжать делать это. Сожалею! Если я когда-нибудь передумаю, я обновлю это дальше, но Outlook не так хорош.

1 голос
/ 24 февраля 2010

Я знаю, NSApplicationMain() читает Info.plist и выполняет какие-то действия с приложением (например, получает исходный файл пера), поэтому я предполагаю, что UIApplicationMain() делает то же самое для iPhone (как получить начальный стиль строки состояния, увеличитьизображение default.png и т. д.).Этот материал больше нигде не раскрывается, поэтому для запуска приложения все равно нужно будет запускать функции без каких-либо побочных эффектов.Ваша единственная ставка - перепроектировать их и скопировать (и надеяться, что все, что они делают, находится в общедоступном SDK).

0 голосов
/ 03 февраля 2010

Почему бы не запустить игровой цикл из UIApplication? (Да, вы не можете иметь его в main(), но это не должно иметь значения.) Некоторые из сообщений делегатов являются хорошими кандидатами.

0 голосов
/ 03 февраля 2010

Является ли целью взять функцию main () и заставить ее работать без изменений на iPhone?

Казалось бы, у вас нет способа полностью изолировать пользователей библиотеки от мысли оПлатформа iPhone - им придется иметь дело с XCode для подписи кода и тому подобного.

Учитывая это, говоря пользователям, что они должны разбить свою функцию main () на несколько частей,может вызывать из applicationDidFinishLaunching и из соответствующего таймера, кажется, это никому не доставит неудобств.

...