2

iPad 1 の 256 MB の RAM にプロセスを適合させながら、base64 エンコーディングで 40 MB 以上の NSString をデコードしてファイルに保存するプロセスを改善する方法についてのアイデアを探しています。

NSXMLParser から NSString を取得します。

id pointerToString;

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
if ([currentElement isEqualToString:@"myElement"]) 
    {
    pointerToString = [string retain];
}
}

次に、コールバックで pointerToString を使用します。

[handler performSelector: action withObject: pointerToString];

コールバック (id 値は pointerToString です)。base64エンコーディングでデコードしながら、pointerToStringでNSDataを初期化します。

^(id value)
{
    if ( [[value class] isSubclassOfClass:[NSString class]] ) 
    {
    NSData *data = [NSData dataFromBase64String:value];
    [data writeToFile:file.path atomically:YES];
}
}

NSData の呼び出し後または呼び出し中にメモリ割り当てが約 130MB に達すると、iPad 1 デバイスのメモリが不足し、iOS によって強制終了されます。

この方法で 40 MB 以上の NSString を処理するには、約 180 MB 以上の RAM が必要であると判断しました (これは iPad 2 および 3 での最大メモリ割り当てであり、より多くの RAM のためにプロセスが機能します)。

アイデア/ヒントはありますか?

ありがとうございました

4

2 に答える 2

2

編集

このサイズのファイルを処理する場合、巨大な入力ファイルもほぼ巨大な出力ファイルも、一度に数メガバイトのファイル全体をメモリにロードしたくない場合があります。これをストリーミング方式で解析しfoundCharacters、メモリに重要な部分を保持せずに、進行中にデータをデコードする必要があります。

ただし、従来の手法では、プロセスの3つのフェーズでXMLファイルメモリ全体を保持できます。

  1. サーバーからXMLファイルをダウンロードすると、

  2. XMLパーサーがそのファイルを解析するとき。と

  3. Base64を実行すると、ファイルがデコードされます。

秘訣は、単一の大きなXMLファイルの小さなチャンクに対して、これら3つのプロセスを同時に実行するストリーミング手法を採用することです。結論として、50 MBのファイル全体をダウンロードするときに、数kbを取得し、XMLを解析します。また、Base64でエンコードされたフィールドを解析する場合は、その数kbに対してBase64デコードを実行し、次の手順に進みます。データのチャンク。

この例(少なくとも、Base64デコードを含まないストリーミングXMLのダウンロードと解析)については、AppleのXMLPerformanceサンプルプロジェクトを参照してください。NSXMLParserよく知られている2つのXMLパーサーと、あまり馴染みのないパーサーが示されていることがわかりますLibXML。の問題NSXMLParserは、それ自体のデバイスに任せて、を使用している場合でも、解析を開始する前にXMLファイル全体をメモリにロードすることですinitWithContentsOfURL

initWithContentsOfURL以前の回答では、を使用すると、NSXMLParserダウンロード中にURLの内容が小さなパケットに解析されると誤って主張しました。プロトコルのfoundCharacters方法は、方法NSXMLParserDelegateと非常に類似しているように思われるので、ダウンロードの進行中に情報を返すという、同じようにストリームを処理することになると確信していました。悲しいことに、そうではありません。NSURLConnectionDelegatedidReceiveDataNSXMLParserNSURLConnection

ただし、Apple XMLPerformanceサンプルプロジェクトのように、を使用LibXMLすることで、実際NSURLConnectionにストリーミング機能を使用して、その場でXMLを解析できます。

私は小さなテストプロジェクトを作成しましたが、AppleのXMLPerformanceサンプルプロジェクトを詳細に確認することをお勧めします。しかし、私の実験では、56MBのXMLファイルは解析と変換の際に100MBをはるかに超えて消費しNSXMLParserましたが、を使用した場合は2MBしか消費しませんでしたLibXML2


コメントでは、Base64でエンコードされたデータをファイルにダウンロードしてからデコードしたいという要望を説明しています。そのアプローチははるかに効率が悪いように見えますが、確かに機能する可能性があります。ちなみに、その最初のダウンロードでは、同じメモリの問題があります(上記で解決しました)。Base64でエンコードされたデータを最初にダウンロードしても、ほとんどのルーチンのようにRAMに簡単にロードされないようにすることをお勧めします。を使用していると仮定して、RAMに保持するのではなく、でデータを受信するときにNSURLConnectionデータをに書き込みます。NSOutputStreamdidReceiveData

(これらのルーチンのほとんどは、適度なサイズのファイルを処理していると想定しているため)にファイルを追加する一般的なパターンではなく、受信時にファイルを書き込む方法の例については、didReceiveResponseAppleのAdvancedURLConnectionsの例のAdvancedGetController.mを参照してください。 NSMutableData)。(認証などに関するAdvancedURLConnectionsサンプルのすべてのものを無視しますが、それがどのように書き込まれるかを理解することに焦点を当てますNSOutputStream。)この手法は、この回答の上部にリストされている3つの問題の最初の問題に対処しますが、後者の2つ。そのためにはLibXML2、AppleのXMLPerformanceサンプルプロジェクトに示されているように、または他の同様の手法を使用することを検討する必要があります。

于 2012-12-04T04:44:55.367 に答える
0

方法

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string

一度にすべてのデータを受信して​​いない可能性があります。ドクターが言ってる

"現在の要素の文字のすべてまたは一部を表す文字列をデリゲートに提供するために、パーサー オブジェクトによって送信されます。 "

したがって、複数回呼び出されます。文字列全体を一度に書き込もうとしているようです (間違っていたらすみません)。したがって、次のようにして、受信したデータをファイルに追加できます。

の組み合わせを使用できます

-writeData: 

-seekToEndOfFile 

NSData をファイルの末尾に書き込むための NSFileHandle クラスのメソッド。

ただし、部分的なデータ受信での base64 エンコーディングには注意してください。

于 2012-12-03T21:33:00.760 に答える