Frage

Ich habe ein bisschen Code, das den Exportieren von Daten aus meiner Anwendung abwickelt. Es nimmt ein NSString voller XML auf und führt es durch ein PHP -Skript aus, um HTML, RTF usw. zu generieren. Es funktioniert gut, es sei denn, ein Benutzer hat eine große Liste. Dies liegt anscheinend darauf, dass es den etwa 8K -Puffer von Nspipe übersteigt.

Ich habe es (glaube ich) im Readpipe und ReadHandle herumgearbeitet, aber ich bin mir nicht sicher, wie ich im WriteHandle/WritePipe damit umgehen soll. Die Bewerbung wird am Strandballe unter [writeHandle writeData:[in... Wenn ich es nicht in GDB breche, warten Sie ein paar Sekunden und fahren Sie dann fort.

Irgendwelche Hilfe, wie ich dies in meinem Code verarbeiten kann?

- (NSString *)outputFromExporter:(COExporter *)exporter input:(NSString *)input {
  NSString *exportedString = nil;
  NSString *path = [exporter path];
  NSTask *task = [[NSTask alloc] init];

  NSPipe *writePipe = [NSPipe pipe];
  NSFileHandle *writeHandle = [writePipe fileHandleForWriting];
  NSPipe *readPipe = [NSPipe pipe];
  NSFileHandle *readHandle = [readPipe fileHandleForReading];

  NSMutableData *outputData = [[NSMutableData alloc] init];
  NSData *readData = nil;

  // Set the launch path and I/O for the task
  [task setLaunchPath:path];
  [task setStandardInput:writePipe];
  [task setStandardOutput:readPipe];

  // Launch the exporter, it will convert the raw OPML into HTML, Plaintext, etc
  [task launch];

  // Write the raw OPML representation to the exporter's input stream
  [writeHandle writeData:[input dataUsingEncoding:NSUTF8StringEncoding]];
  [writeHandle closeFile];

  while ((readData = [readHandle availableData]) && [readData length]) {
    [outputData appendData:readData];
  }

  exportedString = [[NSString alloc] initWithData:outputData encoding:NSUTF8StringEncoding];
  return exportedString;
}
War es hilfreich?

Lösung

Die einfache, schmerzhafte Wahrheit ist, dass das Schreiben vieler Daten an einen Subprozess und dann viele Daten zurücklesen, ohne die Benutzeroberfläche zu blockieren, ohne die Benutzeroberfläche zu blockieren.

Die Lösung ist genauso einfach und sicherlich schmerzhaft-aussehen Interessent: Machen Sie den Export asynchron. Schreiben Sie Daten wie möglich und lesen Sie Daten wie möglich. Sie blockieren nicht nur die Benutzeroberfläche nicht, sondern können auch einen Fortschrittsindikator für einen wirklich langen Export aktualisieren und mehrere Exporte parallel (z. B. aus getrennten Dokumenten) durchführen.

Es ist Arbeit, aber die UI -Auszahlungen sind groß, und das Ergebnis ist sowohl intern als auch extern ein saubereres Design.

Andere Tipps

Es gibt eine neue API seit 10.7, sodass Sie NSNotifikationen vermeiden können.

task.standardOutput = [NSPipe pipe];
[[task.standardOutput fileHandleForReading] setReadabilityHandler:^(NSFileHandle *file) {
    NSData *data = [file availableData]; // this will read to EOF, so call only once
    NSLog(@"Task output! %@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);

    // if you're collecting the whole output of a task, you may store it on a property
    [self.taskOutput appendData:data];
}];

Wahrscheinlich möchten Sie dasselbe oben wiederholen für task.standardError.

WICHTIG:

Wenn Ihre Aufgaben endet, müssen Sie den Lesbarkeitshandler -Block auf NIL festlegen. Andernfalls begegnen Sie eine hohe CPU -Verwendung, da die Lesung niemals aufhören wird.

[task setTerminationHandler:^(NSTask *task) {

    // do your stuff on completion

    [task.standardOutput fileHandleForReading].readabilityHandler = nil;
    [task.standardError fileHandleForReading].readabilityHandler = nil;
}];

Dies ist alles asynchron (und Sie sollten es asynchronisieren), daher sollte Ihre Methode einen Abschlussblock haben.

NSFileHandle VORLAGEDATA scheint unendlich zu blockieren:

Ich habe ein Testprogramm erstellt, das ich mit Nstask aus einem anderen Programm rufe. Ich weise den NSFileHandle Stdin zu und lese die Daten von Pipe. Das Testprogramm überflutet den STDOut mit viel Text mithilfe der NSLog -Funktion. Es scheint keine Möglichkeit zu geben, egal welche API in der NSFileHandle ich benutze, früher oder später die verfügbaren Blöcke, und dann wird die App unendlich hängen und nichts tun. Es bleibt tatsächlich zur Anweisung der Daten gelesen, egal ob es in das while oder darin platziert ist. Ich habe auch versucht, Bytes einzeln zu lesen, hilft auch nicht:

data = [file readDataOfLength: 1];  // blocks infinitely

data = [file availableData]; // blocks infinitely

Dies funktioniert eine Weile, bis es auch gefriert. Es scheint, dass ich bemerkt habe, dass die NSFileHandle -API nicht wirklich mit Shell -Befehlen funktioniert, die viele Daten ausgeben. Daher muss ich mit der Verwendung von POSIX -API um diese verwendete.

Jedes einzelne Beispiel für das Lesen der Daten in Teilen mit dieser API, die aus Stapelüberlauf oder anderen Websites im Internet, entweder synchron oder asynchron, zu blockieren, scheint das letztendliche Lesen zu blockieren.

Um klar zu sein, das ist nicht nur langsam, sondern es wird tatsächlich gefroren, bis Sie mit einem Debugger einbrechen? Es ist kein Problem mit Ihrem Unterprozess?

Man würde erwarten NSFileHandle Um alle Daten zu verarbeiten, die Sie darauf werfen, können Sie Ihre Daten jedoch in kleinere Stücke aufteilen -subdataWithRange: zu sehen, welchen Effekt das hat. Sie könnten auch das schnappen fileDescriptor und verwenden Sie die POSIX -APIs (fdopen, fWrite usw.), um in den Stream zu schreiben. Die POSIX -APIs würde mehr Flexibilität bieten, wenn Sie das tatsächlich brauchen.

Ich glaube, was geschah, war, dass ich in einen durch die verursachten Deadlock geriet, der verursacht wurde [writeHandle writeData:[input dataUsingEncoding:NSUTF8StringEncoding]]; Leitungsfüllung überfüllt sein Puffer und bewirkt, dass die Anwendung hängt, bis sie (nie) geleert wird.

Ich habe es herumgearbeitet, indem ich den Schreibvorgang in einen separaten Thread versandte.

Sich ansehen asynctask.m aus Dieser Kern.

asynctask.m Ermöglicht, mehr als 8.000 Eingabedaten zu verarbeiten.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top