まず、画像(またはバイナリデータ)をCoreDataに保存しないでください。特にiOSで。それをディスクに保存してから、ファイルの場所への参照をCoreDataに保存するとパフォーマンスが大幅に向上します。
次に、サンプルコードは、データをCoreDataに配置する方法を示していません。したがって、解決策を提案することは困難です。
アップデート
私はこれを行う方法への簡単な参照を見つけられなかったので、ここに1つあります:
iOS5.0より前の画像キャッシュ
iOS 5.0より前の環境でディスクに画像キャッシュを設定するには、最初にエンティティに属性を作成しますNSString
。この例では、その属性に名前を付けますimageFilename
。それが完了したらNSManagedObject
、ヘルパーメソッドを実装できるように、エンティティのサブクラスを作成します。
@interface MyEntity : NSManagedObject
@property (nonatomic, retain) NSString *imageFilename;
@property (nonatomic, retain) NSImage *image;
@end
imageFilename
モデルで定義されているため、CoreDataに管理を任せます。ただし、のアクセサを実装しますimage
。
@implementation MyEntity
@dynamic imageFilename;
@synthesize image = _image;
- (void)setImage:(UIImage*)image
{
NSString *filename = [self imageFilename];
if (!filename) {
filename = [[NSProcessInfo processInfo] globallyUniqueString];
[self setImageFilename:filename];
}
[_image release];
_image = [image retain];
NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
NSString *filePath = [cachePath stringByAppendingPathComponent:filename];
NSData *data = UIImagePNGRepresentation(image);
NSError *error = nil;
if (![data writeToFile:filePath options:NSDataWritingAtomic error:&error]) {
NSLog(@"Failed to write image to disk: %@\n%@", [error localizedDescription], [error userInfo]);
return;
}
}
は-setImage:
、イメージをディスクのキャッシュディレクトリに保存します(キャッシュディレクトリはバックアップされておらず、スペースが不足している場合はシステムによって削除される可能性があることに注意してください)。まだ作成されていない場合は、ランダムなファイル名を選択します。
アプリケーションのサンドボックスのディレクトリが変更される可能性があるため、パスは意図的に保存されていません。したがって、ファイル名をCore Dataに保存し、パスを解決するだけです。
また、このエンティティのライフサイクル中にイメージが再度要求された場合にディスクにヒットしないように、イメージへのメモリ参照を保持します。@synthesize
これが、アクセサーを実装しているにもかかわらずの理由です。
画像をPNGとしてディスクに保存することに注意してください。これは費用がかかる可能性がありますが(圧縮ルーチンは比較的遅い)、画像をユニバーサル形式に保つので便利です。
- (UIImage*)image
{
if (_image) return _image;
NSString *filename = [self imageFilename];
if (!filename) return nil;
NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
NSString *filePath = [cachePath stringByAppendingPathComponent:filename];
if (![[NSFileManager defaultFileManager] fileExistsAtPath:filePath]) return nil;
_image = [[UIImage alloc] initWithContentsOfFile:filePath];
return _image;
}
の実装は-image
ほとんど逆です。ファイル名があるかどうかを確認します。フルパスを解決し、画像をメモリにロードしてから、呼び出し元に返します。
- (void)prepareForDeletion
{
[super prepareForDeletion];
NSString *filename = [self imageFilename];
if (!filename) return nil;
NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
NSString *filePath = [cachePath stringByAppendingPathComponent:filename];
NSError *error = nil;
if (![[NSFileManager defaultFileManager] removeItemAtPath:filePath error:&error]) {
NSLog(@"Potential error removing on disk image: %@\n%@", [error localizedDescription], [error userInfo]);
}
}
スペースが不足する状況が発生しないように、キャッシュディレクトリをできるだけクリーンに保ちたいと考えています。したがって、エンティティをCore Dataから削除する場合は、ファイルをディスクから削除します。削除中に発生する実際のエラーは、私たちにとって致命的ではない問題です。ファイルがすでに削除されているなどの理由でエラーの可能性があります。このエラーで完全に失敗する理由はありませんが、ログに記録することが重要です。
- (void)willTurnIntoFault
{
[super willTurnIntoFault];
[_image release], _image = nil;
}
@end
-willTurnIntoFault
最後に、このエンティティのライフサイクルの最後に画像へのメモリ内参照を解放できるように、メソッドを実装します。
画像キャッシュiOS5.0以降
- エンティティにバイナリ属性を作成します。
- 「外部レコードファイルに保存」ビットをオンにします。
- ステップ3はありません