更新:キャッシュの削除は正常に機能します (少なくとも iOS 8.3 以降)。
Apple の「新しい Images.xcassets」も使用することにしました。アプリに約 350 MB の画像があり、アプリが絶えずクラッシュしたとき、事態は悪化し始めました (Retina iPad では、おそらく読み込まれた画像のサイズが原因です)。
3 つの異なるタイプの画像をロードする (プロファイラーを監視する) 非常に単純なテスト アプリを作成しました。
imageNamed:
アセットからロード: 画像がリリースされず、アプリがクラッシュします (私にとっては 400 枚の画像をロードできましたが、実際には画像サイズに依存します)
imageNamed:
(従来はプロジェクトに含まれていました): メモリ使用量が高く、たまに (> 400 画像) への呼び出しが表示されますdidReceiveMemoryWarning:
が、アプリは正常に動作しています。
imageWithContentsOfFile([[NSBundle mainBundle] pathForResource:...)
: イメージは一度に 1 回しかロードされないため、メモリ使用量は非常に低くなります (<20MB)。
画像を何度も表示する必要がある場合、キャッシングは良い考えであるため、メソッドのキャッシングをすべてのせいにするつもりはありませんがimageNamed:
、Apple が資産に対してそれを実装しなかった (または文書化しなかった) のはちょっと悲しいことです実装されていないこと)。imageWithData
私のユースケースでは、ユーザーが画像を再び見ることはないため、非キャッシュを使用します。
私のアプリはほぼ最終版であり、適切な画像を自動的に見つけるための読み込みメカニズムの使用法が非常に気に入っているため、使用法をラップすることにしました。
- project-target-copy-phase から images.xcasset を削除し、すべての画像をプロジェクトと copy-phase に「再度」追加しました (単に Images.xcassets の最上位フォルダーを直接追加し、チェックボックス「追加」 To Target xxx" がチェックされ、"Create groups for any added Folders" がチェックされています (役に立たない Contents.json ファイルについては気にしませんでした)。
- 最初のビルド中に、複数のイメージが同じ名前 (および一貫した方法で名前を変更) の場合、新しい警告をチェックします。
- アプリ アイコンと起動イメージについては、project-target-general で「アセット カタログを使用しない」を設定し、そこで手動で参照します。
- すべての Contents.json ファイルから json-model を生成するシェル スクリプトを作成しました (Apple がアセット アクセス コードで使用する情報を取得するため)。
脚本:
cd projectFolderWithImageAsset
echo "{\"assets\": [" > a.json
find Images.xcassets/ -name \*.json | while read jsonfile; do
tmppath=${jsonfile%.imageset/*}
assetname=${tmppath##*/}
echo "{\"assetname\":\"${assetname}\",\"content\":" >> a.json
cat $jsonfile >> a.json;
echo '},' >>a.json
done
echo ']}' >>a.json
(すべてのデバイスとフォールバック メカニズムの実装は完了していません!!)
#import "UIImage+Extension.h"
#import <objc/objc-runtime.h>
#import "IMGADataModels.h"
@implementation UIImage (UIImage_Extension)
+ (void)load{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = [self class];
Method imageNamed = class_getClassMethod(class, @selector(imageNamed:));
Method imageNamedCustom = class_getClassMethod(class, @selector(imageNamedCustom:));
method_exchangeImplementations(imageNamed, imageNamedCustom);
});
}
+ (IMGABaseClass*)model {
static NSString * const jsonFile = @"a";
static IMGABaseClass *baseClass = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSString *fileFilePath = [[NSBundle mainBundle] pathForResource:jsonFile ofType:@"json"];
NSData* myData = [NSData dataWithContentsOfFile:fileFilePath];
__autoreleasing NSError* error = nil;
id result = [NSJSONSerialization JSONObjectWithData:myData
options:kNilOptions error:&error];
if (error != nil) {
ErrorLog(@"Could not load file %@. The App will be totally broken!!!", jsonFile);
} else {
baseClass = [[IMGABaseClass alloc] initWithDictionary:result];
}
});
return baseClass;
}
+ (UIImage *)imageNamedCustom:(NSString *)name{
NSString *imageFileName = nil;
IMGAContent *imgContent = nil;
CGFloat scale = 2;
for (IMGAAssets *asset in [[self model] assets]) {
if ([name isEqualToString: [asset assetname]]) {
imgContent = [asset content];
break;
}
}
if (!imgContent) {
ErrorLog(@"No image named %@ found", name);
}
if (is4InchScreen) {
for (IMGAImages *image in [imgContent images]) {
if ([@"retina4" isEqualToString:[image subtype]]) {
imageFileName = [image filename];
break;
}
}
} else {
if ( UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone ) {
for (IMGAImages *image in [imgContent images]) {
if ([@"iphone" isEqualToString:[image idiom]] && ![@"retina4" isEqualToString:[image subtype]]) {
imageFileName = [image filename];
break;
}
}
} else {
if (isRetinaScreen) {
for (IMGAImages *image in [imgContent images]) {
if ([@"universal" isEqualToString:[image idiom]] && [@"2x" isEqualToString:[image scale]]) {
imageFileName = [image filename];
break;
}
}
} else {
for (IMGAImages *image in [imgContent images]) {
if ([@"universal" isEqualToString:[image idiom]] && [@"1x" isEqualToString:[image scale]]) {
imageFileName = [image filename];
if (nil == imageFileName) {
// fallback to 2x version for iPad unretina
for (IMGAImages *image in [imgContent images]) {
if ([@"universal" isEqualToString:[image idiom]] && [@"2x" isEqualToString:[image scale]]) {
imageFileName = [image filename];
break;
}
}
} else {
scale = 1;
break;
}
}
}
}
}
}
if (!imageFileName) {
ErrorLog(@"No image file name found for named image %@", name);
}
NSString *imageName = [[NSBundle mainBundle] pathForResource:imageFileName ofType:@""];
NSData *imgData = [NSData dataWithContentsOfFile:imageName];
if (!imgData) {
ErrorLog(@"No image file found for named image %@", name);
}
UIImage *image = [UIImage imageWithData:imgData scale:scale];
DebugVerboseLog(@"%@", imageFileName);
return image;
}
@end