更新:缓存驱逐工作正常(至少从 iOS 8.3 开始)。
我也决定使用 Apple 的“新 Images.xcassets”。事情开始变得糟糕,当我在应用程序中有大约 350mb 的图像并且应用程序不断崩溃时(在 Retina iPad 上;可能是因为加载的图像的大小)。
我编写了一个非常简单的测试应用程序,我在其中加载了三种不同类型的图像(观察分析器):
imageNamed:
从资产加载:图像永远不会被释放并且应用程序崩溃(对我来说,我可以加载 400 张图像,但这实际上取决于图像大小)
imageNamed:
(通常包含在项目中):内存使用率很高,偶尔(> 400 张图像)我看到调用didReceiveMemoryWarning:
,但应用程序运行良好。
imageWithContentsOfFile([[NSBundle mainBundle] pathForResource:...)
:内存使用率非常低(<20mb),因为图像一次只加载一次。
我真的不会责怪imageNamed:
所有方法的缓存,因为如果您必须一次又一次地显示您的图像,缓存是一个好主意,但令人遗憾的是,Apple 没有为资产实现它(或没有记录它)没有实施)。在我的用例中,我将选择非缓存imageWithData
,因为用户不会再看到图像。
由于我的应用程序快要完成了,而且我真的很喜欢使用加载机制来自动找到正确的图像,所以我决定包装一下用法:
- 我从项目目标复制阶段中删除了 images.xcasset 并将所有图像“再次”添加到项目和复制阶段(只需直接添加 Images.xcassets 的顶级文件夹并确保复选框“添加目标 xxx”被选中并“为任何添加的文件夹创建组”(我没有担心无用的 Contents.json 文件)。
- 如果多个图像具有相同的名称(并以一致的方式重命名它们),则在首次构建期间检查新警告。
- 对于应用程序图标和启动图像,在 project-target-general 中设置“不使用资产目录”并在那里手动引用它们。
- 我已经编写了一个 shell 脚本来从所有 Contents.json 文件中生成一个 json 模型(以获取 Apples 在其资产访问代码中使用它的信息)
脚本:
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
- 从 json 输出中删除最后一个“,”逗号,因为我没有费心在这里手动进行。
- 我使用以下应用程序生成 json-model-access 代码:https ://itunes.apple.com/de/app/json-accelerator/id511324989?mt= 12(当前免费),前缀为 IMGA
- 为了不更改正在运行的代码(并希望很快删除我的代码),我使用方法 swizzling 编写了一个不错的类别:
(所有设备和回退机制的实现都没有完成!!)
#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