更新:缓存驱逐工作正常(至少从 iOS 8.3 开始)。
我也决定使用 Apple 的“新 Images.xcassets”。事情开始变得糟糕,当我在应用程序中有大约 350mb 的图像并且应用程序不断崩溃时(在 Retina iPad 上;可能是因为加载的图像的大小)。
我编写了一个非常简单的测试应用程序,我在其中加载了三种不同类型的图像(观察分析器):
imageNamed: 从资产加载:图像永远不会被释放并且应用程序崩溃(对我来说,我可以加载 400 张图像,但这实际上取决于图像大小)
imageNamed:(通常包含在项目中):内存使用率很高,有时(> 400 张图像)我看到对didReceiveMemoryWarning: 的调用,但应用程序运行良好。
imageWithContentsOfFile([[NSBundle mainBundle] pathForResource:...):内存使用率非常低(
我真的不会将 imageNamed: 方法的缓存归咎于所有内容,因为如果您必须一次又一次地显示图像,缓存是一个好主意,但令人遗憾的是 Apple 没有为资产实现它(或者没有记录它没有实现)。在我的用例中,我会选择非缓存imageWithData,因为用户不会再看到图像。
由于我的应用程序快要完成了,我非常喜欢使用加载机制自动找到正确的图像,所以我决定包装一下用法:
- 我从 project-target-copy-phase 中删除了 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
(未完成所有设备和回退机制的实现!!)
#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