Ответ 1
Похоже, я отвечаю на свой вопрос здесь! Я не принимаю свой ответ, пока не смог проверить его на реальном оборудовании и получить его в магазине приложений. Тем не менее, я буду хранить самую последнюю актуальную информацию здесь, в том числе, какие параметры не работают.
Идея № 1. Оказывается, каждый NSRunLoop имеет отношение к потоку. Если я создаю UIApplicationMain в отдельном потоке, он не получит никаких сообщений. В качестве побочного эффекта это делает невозможным определить, когда он завершил инициализацию, поэтому, если там что-то не-потоковое, это просто не сработает. Я могу отправить ему сообщение по потокам, чтобы выяснить, когда он завершил инициализацию, но пока я называю это тупиком.
Идея № 2: UIApplicationMain делает много тонких вещей. Я не уверен, к чему это ограничивается, но я не смог ничего сделать без участия UIApplicationMain. Идея № 2 прямо.
Идея № 3. Получение сигналов ОС важно - вам нужно знать, есть ли наложение телефонного звонка или вы собираетесь выйти. Кроме того, некоторые сообщения об установке кажутся жизненно важными для правильного запуска приложения. Мне не удалось найти какой-либо метод для отправки сообщений, не находясь внутри UIApplicationMain. Единственными параметрами, которые я придумал, были NSRunLoop и CFRunLoop. Ни один из них не работал - сообщения не поступали так, как я хотел. Я не могу использовать это право, но в любом случае Идея № 3 отсутствует.
Совершенно новая сумасшедшая идея № 4. Возможно использование setjmp/longjmp для подделки сопрограмм в C/С++. Хитрость заключается в том, чтобы сначала установить указатель стека на какое-то значение, которое не будет поглощать ничего важного, затем начните вторую процедуру, а затем прыгайте туда-сюда, делая вид, будто у вас есть два стека. Вещи становятся немного грязными, если ваша "вторая сопрограмма" решает вернуться из своей основной функции, но, к счастью, UIApplicationMain никогда не возвращается, так что это не проблема.
Я не знаю, есть ли способ установить указатель стека явно на реальном оборудовании, скажем, на кусок данных, которые я выделил на лету. К счастью, это не имеет значения. По умолчанию iPhone имеет стек 1 МБ, что достаточно просто, чтобы вместить несколько сопрограмм.
То, что я сейчас делаю, это использовать alloca(), чтобы продвинуть указатель стека вперед на 768 килобайт, а затем порождать UIApplicationMain, а затем использовать setjmp/longjmp для возврата назад и вперед между моей "программой UI" и моей "основной рутиной", Пока это работает.
Предостережения:
-
Невозможно узнать, когда "подпрограмма UI" не имеет сообщений для обработки, и когда у нее нет сообщений для обработки, она будет просто блокироваться бесконечно, пока это больше не будет. Я решаю это, создавая таймер, который запускает каждые 0,1 миллисекунды. Каждый раз, когда таймер срабатывает, я бросаю свою "основную рутину", делаю одиночный игровой цикл, а затем возвращаюсь обратно в "режим пользовательского интерфейса" для другого таймера. Чтение документации показывает, что она не будет складывать "таймерные вызовы" на неопределенный срок. Кажется, я получаю сообщение "прекратить", хотя мне еще не удалось проверить его до сих пор, и я не тестировал никаких других важных сообщений. (К счастью, всего четыре сообщения, и один из них связан с установкой.)
-
Большинство современных ОС не будут выделять весь стек одновременно. Возможно, iPhone является одним из них. То, что я не знаю, это то, что, скажем, напасть на указатель стека 3/4 мега-форварда будет выделять все "за ним". Если это так, я могу эффективно тратить 3/4 мегабайта ОЗУ, что на iPhone значительно. Это можно было бы обработать, нажимая указатель вперед на меньшую величину, но это действительно ухаживает за размером стека - оно эффективно ограничивает ваш стек до того, как вы набросите указатель вперед, и вам придется разобраться в этом заранее. Некоторые дозорные данные в стеке, в сочетании с хорошим мониторингом и системой протоколирования для проблем с размером стека, вероятно, могут решить эту проблему, но это нетривиальная проблема. (В качестве альтернативы, если я смогу понять, как уклониться от указателя стека непосредственно на собственное оборудование, я могу просто malloc()/new [] несколько килобайт, указать на него указатель стека и использовать его в качестве нового стека. Мне нужно выяснить, сколько места ему нужно, но я сомневаюсь, что это будет много, учитывая, что это не очень много.)
-
В настоящий момент это не проверено на самом аппаратном обеспечении (дайте ему неделю или две, я сначала заработал другой проект.)
-
Я не знаю, сможет ли Apple выяснить, что я делаю, и нанести на нее гигантскую REJECTED наклейку, когда я попытаюсь отправить в магазин приложений. Это, скажем так, немного вне их намерений для API. Пальцы пересекли.
Я буду держать этот пост обновленным и официально принять его, как только я проверил, что он, как вы знаете, работает.
Позднее обновление: меня отвлекало множество других вещей. С тех пор у меня было несколько изменений, которые заставляют меня гораздо меньше интересоваться развитием Apple. Мой нынешний подход не показал признаков неработоспособности, но на самом деле у меня нет мотивации, чтобы сохранить его. Сожалею! Если я когда-нибудь передумаю, я обновлю это дальше, но Outlook не так хорош.