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