21

根据苹果文档,建议将 xcassets 用于 iOS7 应用程序并通过 imageNamed 引用这些图像。

但据我所知,imageNamed 和内存总是存在问题。

所以我做了一个简短的测试应用程序 - 使用 imageNamed 从 xcassets 目录中引用图像并启动分析器......结果符合预期。即使在我从超级视图中删除 ImageView 并将其设置为 nil 之后,分配的内存也不会再次释放。

我目前正在开发一个包含许多大图像的 iPad 应用程序,这种奇怪的 imageView 行为会导致内存警告。

但在我的测试中,我无法通过 imageWithContentsOfFile 访问 xcassets 图像。

那么在 iOS7 上处理大图像的最佳方法是什么?有没有办法以另一种(更高性能)的方式访问 xcassets 目录中的图像?或者我根本不应该使用 xcassets 以便可以使用 imageWithContentsOfFile?

谢谢您的回答!

4

1 回答 1

16

更新:缓存驱逐工作正常(至少从 iOS 8.3 开始)。

我也决定使用 Apple 的“新 Images.xcassets”。事情开始变得糟糕,当我在应用程序中有大约 350mb 的图像并且应用程序不断崩溃时(在 Retina iPad 上;可能是因为加载的图像的大小)。

我编写了一个非常简单的测试应用程序,我在其中加载了三种不同类型的图像(观察分析器):

  1. imageNamed:从资产加载:图像永远不会被释放并且应用程序崩溃(对我来说,我可以加载 400 张图像,但这实际上取决于图像大小)

  2. imageNamed:(通常包含在项目中):内存使用率很高,偶尔(> 400 张图像)我看到调用didReceiveMemoryWarning:,但应用程序运行良好。

  3. 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
于 2014-05-02T22:54:15.637 回答