14

HTTP 経由で大量の画像を取得する必要がある iOS アプリを実装しています。私はいくつかのアプローチを試しましたが、独立して何をしたかというと、Instuments はメモリ割り当てが常に増加していることを示しており、デバイスで実行すると遅かれ早かれアプリがクラッシュします。インスツルメンツによって示された漏れはありません。

これまでのところ、次のアプローチを試しました。

  • NSOperation 内で同期 NSURLConnection を使用して画像を取得する
  • NSOperation 内で非同期 NSURLConnection を使用して画像を取得します
  • メインスレッドで [NSData dataWithContentsOfURL:url] を使用して画像を取得します
  • NSOperation 内で同期 ASIHTTPRequest を使用して画像をフェッチする
  • 非同期 ASIHTTPRequest を使用して画像をフェッチし、それを NSOperationQueue に追加します
  • 非同期 ASIHTTPRequest と completionBlock を使用して画像をフェッチする

Instrumetns のコール ツリーは、HTTP 応答の処理中にメモリが消費されることを示しています。非同期 NSURLConnection の場合、これは

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[receivedData appendData:data];
}  

同期 NSURLConnection の場合、Instruments は CFData (ストア) エントリの増加を示します。ASIHTTPRequest の問題は、類似のコード位置にある非同期 NSURLConnection の問題と同じようです。[NSData dataWithContentsOfURL:url] アプローチは、まさにそのステートメントでメモリ割り当ての合計量が増加していることを示しています。

リクエストが別のスレッドで行われたときに NSAutoReleasePool を使用していて、[[NSURLCache sharedURLCache] removeAllCachedResponses] でメモリを解放しようとしましたが、成功しませんでした。

問題を解決するためのアイデア/ヒントはありますか? ありがとう。

編集: CoreDataを使用して画像を永続化する場合にのみ、動作が表示されます。NSInvocationOperation として実行するコードは次のとおりです。

-(void) _fetchAndSave:(NSString*) imageId {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString *url = [NSString stringWithFormat:@"%@%@", kImageUrl, imageId];
HTTPResponse *response = [SimpleHTTPClient GET:url headerOrNil:nil];
NSData *data = [response payload];

if(data && [data length] > 0) {
    UIImage *thumbnailImage = [UIImage imageWithData:data];
    NSData *thumbnailData = UIImageJPEGRepresentation([thumbnailImage scaleToSize:CGSizeMake(55, 53)], 0.5); // UIImagePNGRepresentation(thumbnail); 

    [self performSelectorOnMainThread:@selector(_save:) withObject:[NSArray arrayWithObjects:imageId, data, thumbnailData, nil] waitUntilDone:NO];
}
[pool release];
}

CoreData に関連するものはすべてここのメイン スレッドで実行されるため、CoreData のマルチスレッドの問題は発生しません。ただし、画像を永続化すると、Instruments は上記の位置でメモリ割り当てが絶えず増加していることを示します。

編集 II:

CoreData 関連のコード:

-(void) _save:(NSArray*)args {
NSString *imageId = [args objectAtIndex:0];
NSData *data = [args objectAtIndex:1];
NSData *thumbnailData = [args objectAtIndex:2];

Image *image = (Image*)[[CoreDataHelper sharedSingleton] createObject:@Image];
image.timestamp =  [NSNumber numberWithDouble:[[NSDate date] timeIntervalSince1970]];
image.data = data;

Thumbnail *thumbnail = (Thumbnail*)[[CoreDataHelper sharedSingleton] createObject:@"Thumbnail"];
thumbnail.data = thumbnailData;
thumbnail.timestamp = image.timestamp;
[[CoreDataHelper sharedSingleton] save];
}

CoreDataHelper から (self.managedObjectContext は、現在のスレッドで使用可能な NSManagedObjectContext を選択しています):

-(NSManagedObject *) createObject:(NSString *) entityName {
return [NSEntityDescription insertNewObjectForEntityForName:entityName inManagedObjectContext:self.managedObjectContext];
}
4

2 に答える 2

9

同様の問題がありました。http 経由で大量の画像をフェッチしている間に、メモリ割り当てが大幅に増加し、鋸歯状のパターンが発生しました。システムが進むにつれて、多かれ少なかれクリーンアップするのが見られますが、ゆっくりと、予測どおりではありません. その間、ダウンロードがストリーミングされ、メモリに保持されていたものに積み重なっていきました. メモリの割り当ては 200M あたりに達し、それから私たちは死んでしまいます。

問題は NSURLCache の問題でした。[[NSURLCache sharedURLCache] removeAllCachedResponses] を試したと述べました。私たちもそれを試みましたが、少し違うことを試みました。

ダウンロードはN 個の画像/動画のグループで行われます。Nは通常 50 から 500 です。Nのすべてをアトミック操作として取得することが重要でした。

http ダウンロードのグループを開始する前に、次のことを行いました。

NSURLCache *sharedCache = [[NSURLCache alloc] initWithMemoryCapacity:0 diskCapacity:0 diskPath:0];
[NSURLCache setSharedURLCache:sharedCache];

次に、同期呼び出しを使用して http 経由でNの各画像を取得します。このグループ ダウンロードは NSOperation で行うため、UI をブロックしていません。

NSData *movieReferenceData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error]; 

最後に、個々の画像をダウンロードするたびに、その画像の NSData オブジェクトを使い終わったら、次のように呼び出します。

[sharedCache removeAllCachedResponses]; 

私たちのメモリ割り当てのピーク動作は、非常に快適な数メガバイトにまで落ち込み、成長を止めました。

于 2012-02-29T22:12:47.520 に答える
0

この場合、見るべきものを正確に見ています。-[NSMutableData appendData:]内部バッファのサイズを増やして、新しいデータを保持します。は常にメモリ内に配置されるためNSMutableData、これに対応してメモリ使用量が増加します。何を期待していましたか?

これらのイメージの最終的な宛先がディスク上にある場合は、 のNSOutputStream代わりに を使用してみてくださいNSMutableData。その後、画像を表示したい場合は、UIImage完了時にファイルへのポイントを作成できます。

于 2011-02-07T14:38:49.313 に答える