2011-09-28 3 views
-1

Je suis nouveau à l'objectif c. J'utilise le code suivant pour exécuter l'API de ligne de commande dans l'objectif c. le code fonctionne bien pour moi. mais pourquoi ce code utilise NSRunLoop?API de ligne de commande et waitForDataInBackgroundAndNotify

-(void)uploadData 
{ 
setenv([@"PASSWORD" UTF8String], [mPassword UTF8String], 1); 
[task setLaunchPath:executablePathRoot]; 
[task setArguments:array]; 
NSPipe *pipe = [NSPipe pipe]; 
NSPipe *errorPipe = [NSPipe pipe]; 
[task setStandardOutput:pipe]; 
[task setStandardError:errorPipe]; 
//keeps your log where it belongs 
//[task setStandardInput:[NSPipe pipe]]; 

NSFileHandle *outFile = [pipe fileHandleForReading]; 
NSFileHandle *errFile = [errorPipe fileHandleForReading]; 


[task launch]; 
[[NSNotificationCenter defaultCenter] addObserver:self 
             selector:@selector(terminated:) 
              name:NSTaskDidTerminateNotification 
              object:task]; 

[[NSNotificationCenter defaultCenter] addObserver:self 
             selector:@selector(outData:) 
              name:NSFileHandleDataAvailableNotification 
              object:outFile]; 

[[NSNotificationCenter defaultCenter] addObserver:self 
             selector:@selector(errData:) 
              name:NSFileHandleDataAvailableNotification 
              object:errFile]; 


[outFile waitForDataInBackgroundAndNotify]; 
[errFile waitForDataInBackgroundAndNotify]; 
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; 
while(!terminated) 
{ 
    if (![[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) 
    { 
     break; 
    } 
    [pool release]; 
    pool = [[NSAutoreleasePool alloc] init]; 
} 
[pool release]; 

[self appendDataFrom:outFile to:output]; 
[self appendDataFrom:errFile to:error]; 
//[task waitUntilExit]; 
[task release]; 
} 


-(void) outData: (NSNotification *) notification 
{ 
NSLog(@"outData"); 
NSFileHandle *fileHandle = (NSFileHandle*) [notification object]; 
[self appendDataFrom:fileHandle to:output]; 
[fileHandle waitForDataInBackgroundAndNotify]; //Checks to see if data is available in a background thread. 
} 


-(void) errData: (NSNotification *) notification 
{ 
NSLog(@"errData"); 
NSFileHandle *fileHandle = (NSFileHandle*) [notification object]; 
[self appendDataFrom:fileHandle to:output]; 
[fileHandle waitForDataInBackgroundAndNotify]; 
} 

- (void) terminated: (NSNotification *)notification 
{ 
NSLog(@"Task terminated"); 
[[NSNotificationCenter defaultCenter] removeObserver:self]; 
terminated =YES; 
} 

Répondre

4

Ceci permet de synchroniser la méthode uploadData. L'exécution ne peut pas quitter la boucle while jusqu'à ce que le drapeau terminated soit défini sur OUI. L'appel suivant

[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]] 

permet à l'application de traiter d'autres événements. Ainsi, lorsque le NSTaskDidTerminateNotification a reçu le drapeau terminé sera changé et la boucle while se termine.

Cependant, il n'est pas nécessaire d'allouer un pool d'autorelease ici. En outre, le code est excessif. Il peut beaucoup plus simple:

while(!terminated) 
{ 
    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; 
} 

Mise à jour: La prise en compte des commentaires de ughoavgfhw, faisons l'appel encore plus sûr en incluant le résultat de RunMode: beforeDate: dans l'état.

while(!terminated && [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]); 
+1

Je ne suis pas d'accord sur vos modifications du code de la boucle d'exécution. Vous devriez toujours utiliser un pool autorelease autour d'une boucle d'exécution, puisque vous ne savez jamais combien de données seront créées et auto libérées dans une itération. Il est également approprié de rompre la boucle si 'runMode: beforeDate:' renvoie 'NO', car cela indique une erreur (ou aucune source d'entrée, ce qui est une erreur puisque les tuyaux doivent être connectés). – ughoavgfhw

+0

@ughoavgfhw Je suis d'accord qu'un pool autorelease peut être utilisé si nous savons ce que nous allons optimiser. Cependant, je suis certain que nous ne devrions pas toujours l'utiliser comme moyen préventif simplement parce que nous ne savons pas combien de données seront créées. Nous avons déjà un pool d'autorelease dans le thread actuel, il fera l'affaire. – Davyd

+0

@ughoavgfhw Je suis d'accord, le résultat négatif de runMode: beforeDate: devrait être géré. – Davyd