Почему функция main () приложения iPhone никогда не завершается? - PullRequest
9 голосов
/ 18 февраля 2009

Рассмотрим следующий метод main(), который вы найдете в большинстве приложений для iPhone:

int main(int argc, char *argv[])
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    int retVal = UIApplicationMain(argc, argv, nil, nil);
    [pool release];
    return retVal;
}

В каждом приложении iPhone, которое я запускал в Simulator с этими приложениями (включая несколько примеров проектов, предоставленных Apple), поток никогда не завершает работу UIApplicationMain(), а любой оставшийся код в main() никогда не выполняется. Это ожидаемое поведение?

Я проверил, что операторы после UIApplicationMain() никогда не выполняются, шагая по коду с помощью отладчика. Когда пользователь останавливает приложение (например, нажав кнопку «Домой»), полученная трассировка стека показывает, что в конечном итоге вызывается [UIApplication _terminateWithStatus:]. Эта функция вызывает метод applicationWillTerminate: вашего делегата приложения. Как только это заканчивается, [UIApplication _terminateWithStatus:], кажется, убивает / выходит из потока.

Может ли кто-то подтвердить, что именно так main() должен работать, или хотя бы подтвердить то же поведение на своей машине?

Ответы [ 6 ]

19 голосов
/ 19 февраля 2009

Первоначальный вопрос был: " Почему у основной функции () приложения iPhone никогда не получается завершить? "

Краткий ответ: поскольку UIApplicationMain () закодирован так, что он никогда не возвращается.

Проведя несколько тестов в Simulator и на устройстве и попросив другого разработчика выполнить те же тесты, я подтвердил, что UIApplicationMain никогда не возвращается. Когда пользователь обычно завершает приложение, нажимая кнопку «Домой», программа в конечном итоге завершает работу в неопубликованном методе UIApplication, который называется _terminateWithStatus. Этот метод вызывает exit (0).

Это поведение соответствует поведению функции NSApplicationMain (которая является версией функции UIApplicationMain AppKit / Cocoa). В документации для NSApplicationMain () четко указано, что он никогда не вернется.

Я отправил в Apple сообщение об ошибке (6600198) с просьбой исправить официальную документацию (и шаблон Xcode для main.m), указав, что UIApplicationMain () никогда не вернется. Хотя это не является функциональной проблемой, текущий шаблон и документы вводят в заблуждение.

Спасибо всем за вклад и мозговой штурм!

3 голосов
/ 18 февраля 2009

Попробуйте:

int main(int argc, char *argv[])
{
    NSLog(@"Step 0");
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    NSLog(@"Step 1");
    int retVal = UIApplicationMain(argc, argv, nil, nil);
    NSLog(@"Step 2");
    [pool release];
    NSLog(@"Step 3");
    return retVal;
}

Возможно, освобождение пула препятствует дальнейшей регистрации, в этом случае вы получите шаг 2, но не шаг 3.

Если шаг 2 не печатается, то это почти наверняка что-то не так с UIApplicationMain - есть вероятность, что он не вернется, поэтому поместите операторы NSLog (шаг 1.1, шаг 1.2, ...) в различные точки внутри него и запустите, чтобы найти последнее зарегистрированное сообщение.

Продолжайте детализацию (Шаг 1.7.1, 1.7.2, .... 1.7.6.3.2, ...) - в конце концов, вы будете отслеживать точную линию (как бы глубоко в иерархии вызовов), когда сообщения журнала перестают регистрироваться, и эта строка станет вашим виновником (либо «отключение» входа, либо выход без обычного возврата).

Еще один фрагмент, который я нашел в Интернете:

=====

Когда вы используете эту строку:

int retVal = UIApplicationMain(argc, argv, @"MyApp", @"MyApp");

Первый MyApp - это ваш основной класс делегатов приложения. Второй - это класс, куда SpringBoard отправляет сенсорные уведомления.

Кроме того, если вы используете SDK и у вас есть основной кончик, определенный в Info.plist, вы можете оставить вызов как:

int retVal = UIApplicationMain(argc, argv, nil, nil);

как все, что будет рассмотрено, когда вы создадите свои xibs.

=====

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

Тем не менее, моя первая мысль при чтении заключается в том, что Springboard будет вызывать ваш класс делегата при нажатии кнопок, чтобы попросить вас что-то сделать (например, отключить изящно). Если он не может спросить вас (т. Е. Не имеет ни одного делегата), возможно, он имеет право закрыть вас так, как считает нужным, например, с [UIApplication _terminateWithStatus:].

В мире Windows вы, вероятно, отправили бы сообщение о выходе в главное окно, но, как я уже сказал, разработка для iPhone может отличаться.

Тем не менее, это проспект для расследования. Мне было бы интересно узнать, какие звонки были сделаны делегату, если вы его предоставили. Код, включенный в приведенный выше фрагмент, имеет следующий код:

@implementation MyApp
- (void) applicationDidFinishLaunching:(id)unused {
    rect = [ UIHardware fullScreenApplicationContentRect ];
    rect.origin.x = 0.0f;
    rect.origin.y = 0.0f;
    window = [ [ UIWindow alloc ] initWithContentRect: rect ];
    [ window makeKeyAndVisible ];
    view = [ [ MyAppView alloc ] initWithFrame: rect ];
    [ window setContentView: view ];
}
- (void) dealloc {
    [ window release ];
    [ view release ];
    [ super dealloc ];
}

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

1 голос
/ 18 февраля 2009

После вызова функции UIApplicationMain ваше приложение запускается (установление цикла запуска и т. Д.), И тогда вся работа должна выполняться вне контекста main (если вам нужно, чтобы он запускался в main, сделайте это до этого момента). При выходе из приложения, как правило, более эффективно разрешать ОС выполнять очистку памяти.

1 голос
/ 18 февраля 2009

пытается использовать fprintf и посмотреть, что происходит

int main(int argc, char *argv[])
{
    /*
       ... 
     same as above
       ... 
    */
    [pool release];
    char file_name = "/tmp/log"

    FILE *file = fopen(file_name, "w");

    fprintf(file_name, "END\n");
}

и расскажите нам, что происходит

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

в GDB до

b main.c:x 

где x - номер строки оператора возврата

1 голос
/ 18 февраля 2009

После [релиза пула] нечего войти?

0 голосов
/ 22 августа 2009

У меня тоже нет опыта возврата. И установите контрольные точки для проверки точно так, как сказал Клинт.

У мудреца есть хорошая точка.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...