objective c - tutorial - Perché la funzione main() di un'app per iPhone non ha mai la possibilità di finire?




swift programming tutorial (4)

Dopo [rilascio pool] non c'è niente per accedere?

Considera il seguente metodo main() che troverai nella maggior parte delle applicazioni iPhone:

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

In ogni app per iPhone che ho eseguito in Simulator con questi (inclusi diversi progetti di esempio forniti da Apple), il thread non esce mai da UIApplicationMain() e qualsiasi codice rimanente in main() non viene mai eseguito. È questo comportamento previsto?

Ho verificato che le istruzioni dopo UIApplicationMain() non UIApplicationMain() mai eseguite passando il codice con un debugger. Quando l'utente interrompe un'applicazione (premendo il pulsante "Home", ad esempio), la traccia dello stack risultante mostra che alla fine viene chiamato [UIApplication _terminateWithStatus:] . Questa funzione richiama il metodo applicationWillTerminate: dell'applicazione delegato applicationWillTerminate: . Al termine, [UIApplication _terminateWithStatus:] sembra uccidere / uscire dal thread.

Qualcuno può confermare che questo è il modo in cui main() dovrebbe funzionare, o almeno confermare lo stesso comportamento sulla loro macchina?


Dopo aver chiamato la funzione UIApplicationMain , la tua applicazione si avvia (stabilendo un runloop, ecc.) E tutto il lavoro deve essere eseguito al di fuori del contesto principale (se ne hai bisogno per l'esecuzione in main, fallo prima di quel punto). Quando si esce da un'applicazione è generalmente più efficiente consentire al sistema operativo di eseguire la pulizia della memoria.


provando a usare fprintf e vedere cosa succede

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");
}

e dicci cosa succede

Ho anche pensato che il modo più semplice per verificare era quello di impostare un punto di interruzione proprio al momento del ritorno

in gdb do

b main.c:x 

dove x è il numero di riga dell'istruzione return


Provare:

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;
}

È possibile che il rilascio del pool impedisca ulteriori registrazioni, nel qual caso si otterrà il passaggio 2 ma non il passaggio 3.

Se il passo 2 non viene stampato, allora è quasi certamente qualcosa di sbagliato con UIApplicationMain - c'è una possibilità che non ritorni quindi metti le istruzioni NSLog (passo 1.1, passo 1.2, ...) in vari punti all'interno di esso e corri a trova l'ultimo messaggio registrato.

Continuate a eseguire il drill-down (passo 1.7.1, 1.7.2, .... 1.7.6.3.2, ...) - alla fine, rintraccerete la linea esatta (comunque profonda nella gerarchia di chiamate) quando i messaggi di registro smetti di essere loggato e quella linea sarà il tuo colpevole (o "spegnendo" il logging o uscendo senza tornare normalmente).

Un altro frammento che ho trovato sul web:

=====

Quando usi questa linea:

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

La prima MyApp è la tua classe di delegati dell'app principale. Il secondo è la classe in cui SpringBoard invia notifiche di tocco.

Inoltre, se si utilizza l'SDK e si dispone di un pennino principale definito in Info.plist, è possibile lasciare la chiamata come:

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

come tutto ciò che sarà coperto quando crei i tuoi xibs.

=====

Ora non ne so abbastanza dello sviluppo dell'iPhone (in particolare xibs) per sapere cosa significa quel ultimo bit (o se lo hai impostato correttamente) ma suona come un'altra fase di compilazione.

Tuttavia, il mio primo pensiero dalla lettura è che Springboard chiamerà la classe del tuo delegato quando i pulsanti vengono premuti per chiederti di fare qualcosa (come chiudere con grazia). Se non può chiederti (cioè, nessun delegato), probabilmente è nei suoi diritti [UIApplication _terminateWithStatus:] come [UIApplication _terminateWithStatus:] , come con [UIApplication _terminateWithStatus:] .

Nel mondo Windows, probabilmente invieresti un messaggio di chiusura alla finestra principale ma, come ho detto, lo sviluppo di iPhone potrebbe essere diverso.

Eppure, è una strada da investigare. Sarei interessato a vedere quali chiamate sono state fatte a un delegato se ne hai fornito uno. Il codice incluso con lo snippet qui sopra aveva questo:

@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 ];
}

Quindi forse un delegato con dealloc() è il segreto per farlo tornare a main() . Perché non dai uno scatto? Potrebbe avvicinarti al tuo obiettivo anche se non risolve il problema principale.