3

アプリケーションからのデータのエクスポートを処理するコードが少しあります。XMLでいっぱいのNSStringを取り込んで、PHPスクリプトを介して実行し、HTM1、RTFなどを生成します。ユーザーが大きなリストを持っていない限り、うまく機能します。これは明らかに、NSPipeの8k程度のバッファをオーバーランしているためです。

readPipeとreadHandleで(私は思うに)回避しましたが、writeHandle/writePipeでどのように処理するかわかりません。アプリケーションは[writeHandle writeData:[in...、gdbで中断しない限り、ビーチボールになります。数秒待ってから続行します。

コードでこれを回避する方法について何か助けはありますか?

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

6 に答える 6

11

10.7 以降の新しい API があるため、NSNotifications の使用を避けることができます。

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

おそらく、 に対して上記と同じことを繰り返したいと思うでしょうtask.standardError

重要:

タスクが終了したら、readabilityHandler ブロックを nil に設定する必要があります。そうしないと、読み取りが停止しないため、CPU 使用率が高くなります。

[task setTerminationHandler:^(NSTask *task) {

    // do your stuff on completion

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

これはすべて非同期であるため (非同期にする必要があります)、メソッドには ^completion ブロックが必要です。

于 2013-04-29T08:50:26.237 に答える
4

NSFileHandle availableData は無限にブロックしているようです:

別のプログラムから NSTask で呼び出すテスト プログラムを作成しました。NSFileHandle を stdin に割り当て、パイプからデータを読み取ります。テスト プログラムは、NSLog 関数を使用して多くのテキストで stdout をあふれさせます。NSFileHandle でどの API を使用しても、遅かれ早かれ availableData がブロックされ、アプリが無限にハングアップして何もしなくなります。while に配置されているか、その中に配置されているかに関係なく、実際にはデータ読み取りステートメントで停止します。私もバイトを1つずつ読んでみましたが、どちらも役に立ちません:

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

data = [file availableData]; // blocks infinitely

これは、フリーズするまでしばらく機能します。NSFileHandle API は、大量のデータを出力するシェル コマンドでは実際には機能しないことに気付いたようです。そのため、代わりに Posix API を使用してこれを回避する必要があります。

Stack Overflow またはインターネットの他のサイトから、この API を使用してデータを部分的に読み取る方法の例はすべて、同期的または非同期的に、最後の fileAvailableData 読み取りまでブロックされているようです。

于 2012-08-24T11:12:06.473 に答える
1

単純で痛ましい真実は、サブプロセスに大量のデータを書き込み、そこから大量のデータを読み取ることは、UI をブロックせずに 1 つの関数またはメソッドで実行できることではないということです。

解決策も同様に単純で、確かに難しそうに見えます: エクスポートを非同期にします。可能な限りデータを書き込み、可能な限りデータを読み取ります。UI をブロックしないだけでなく、非常に長いエクスポートの進行状況インジケーターを更新したり、(別々のドキュメントなどから) 複数のエクスポートを並行して実行したりすることもできます。

それはうまくいきますが、UI のメリットは大きく、その結果、内部的にも外部的にもすっきりしたデザインになります。

于 2009-09-09T11:00:54.790 に答える
0

明確にするために、これは遅いだけでなく、デバッガーで侵入するまで実際にフリーズしていますか? サブプロセスの問題ではありませんか?

スローされたデータを処理することが期待NSFileHandleされますが、おそらく、 を使用してデータを小さなチャンクに分割して、それが-subdataWithRange:どのような影響を与えるかを確認できます。を取得しfileDescriptorて POSIX API (fdopen、fwrite など) を使用して、ストリームに書き込むこともできます。実際にそれが必要な場合は、POSIX API の方が柔軟性が高くなります。

于 2009-09-08T22:42:55.383 に答える
0

この要点asynctask.mから見てください。

asynctask.m8k 以上の入力データを処理できます。

于 2013-01-18T14:44:44.367 に答える
0

何が起こっていたのかは、[writeHandle writeData:[input dataUsingEncoding:NSUTF8StringEncoding]];行がいっぱいになり、バッファがいっぱいになり、アプリケーションが(決して)空になるまでハングする原因となったデッドロックに遭遇したためだと思います。

書き込み操作を別のスレッドにディスパッチすることで、この問題を回避しました。

于 2009-09-15T20:21:52.550 に答える