使用没有imageNamed的xcassets来防止内存问题? [英] use xcassets without imageNamed to prevent memory problems?

查看:160
本文介绍了使用没有imageNamed的xcassets来防止内存问题?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

according to the apple documentation it is recommended to use xcassets for iOS7 applications and reference those images over imageNamed.

但据我所知,有总是出现图像命名和内存问题。

But as far as I'm aware, there were always problems with imageNamed and memory.

所以我做了一个简短的测试应用程序 - 用imageNamed从xcassets目录中引用图像并启动了profiler ...结果如下预期。一旦分配的内存没有再次发布,即使我从superview中删除了ImageView并将其设置为nil。

So I made a short test application - referencing images out of the xcassets catalogue with imageNamed and started the profiler ... the result was as expected. Once allocated memory wasn't released again, even after I removed the ImageView from superview and set it to nil.

我目前正在处理一个有很多大的iPad应用程序图像和这种奇怪的imageView行为会导致内存警告。

I'm currently working on an iPad application with many large images and this strange imageView behavior leads to memory warnings.

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

But in my tests I wasn't able to access xcassets images over imageWithContentsOfFile.

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

So what is the best approach to work with large images on iOS7? Is there a way to access images from the xcassets catalogue in another (more performant) way? Or shouldn't I use xcassets at all so that I can work with imageWithContentsOfFile?

感谢您的回答!

推荐答案

更新:缓存逐出罚款(至少自iOS 8.3起)。

UPDATE: Cache eviction works fines (at least since iOS 8.3).

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

I decided to go with the "new Images.xcassets" from Apple, too. Things started to go bad, when I had about 350mb of images in the App and the App constantly crashed (on a Retina iPad; probably because of the size of the loaded images).

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

I have written a very simple test app where I load the images in three different types (watching the profiler):


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

  1. imageNamed: loaded from an asset: images never gets released and the app crashes (for me I could load 400 images, but it really depends on the image size)

imageNamed:(通常包含在项目中):内存使用量很高,偶尔会有一段时间(> 400图像)我看到对 didReceiveMemoryWarning:的调用,但应用程序运行正常。

imageNamed: (conventionally included to the project): The memory usage is high and once in a while (> 400 images) I see a call to didReceiveMemoryWarning:, but the app is running fine.

imageWithContentsOfFile([[NSBundle mainBundle] pathForResource:...):内存使用率非常高低(<20mb),因为图像一次只加载一次。

imageWithContentsOfFile([[NSBundle mainBundle] pathForResource:...): The memory usage is very low (<20mb) because the images are only loaded once at a time.

我真的不会责怪缓存 imageNamed:方法,因为缓存是一个好主意,如果你不得不一次又一次地展示你的图像,但苹果公司没有实现它有点令人遗憾对于资产(或没有记录它没有实现)。在我的用例中,我将选择非缓存 imageWithData ,因为用户不会再看到图像。

I really would not blame the caching of the imageNamed: method for everything as caching is a good idea if you have to show your images again and again, but it is kind of sad that Apple did not implement it for the assets (or did not document it that it is not implemented). In my use-case, I will go for the non-caching imageWithData because the user won't see the images again.

由于我的应用程序几乎是最终的,我非常喜欢使用加载机制自动找到正确的图像,我决定用它来包装:

As my app is almost final and I really like the usage of the loading mechanism to find the right image automatically, I decided to wrap the usage:


  • 我从project-target-copy-phase中删除了images.xcasset,并将所有图像再次添加到项目和复制阶段(只需直接添加Images.xcassets的顶级文件夹并制作确保选中添加到目标xxx复选框并为任何添加的文件夹创建组(我没有打扰无用的Contents.json文件)。

  • 在首次构建检查期间如果多个图像具有相同的名称(并以一致的方式重命名),则发出新警告。

  • 对于App图标和启动图像在项目目标中设置不使用资产目录 -general并在那里手动引用它们。

  • 我编写了一个shell脚本来生成json-m来自所有Contents.json文件的odel(以获取Apples在其资产访问代码中使用它的信息)

  • I removed the images.xcasset from the project-target-copy-phase and added all images "again" to the project and the copy-phase (simply add the top level folder of Images.xcassets directly and make sure that the checkbox "Add To Target xxx" is checked and "Create groups for any added folders" (I did not bother about the useless Contents.json files).
  • During first build check for new warnings if multiple images have the same name (and rename them in a consistent way).
  • For App Icon and Launch Images set "Don't use asset catalog" in project-target-general and reference them manually there.
  • I have written a shell script to generate a json-model from all the Contents.json files (to have the information as Apples uses it in its asset access code)

脚本:

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模型访问代码: https://itunes.apple.com/de/app/json-accelerator/id511324989?mt=12 (目前免费),前缀为IMGA

  • 我已经编写了一个很好的类别,使用方法调配,以便不更改正在运行的代码(并希望很快删除我的代码):

    • Remove the last "," comma from json output as I did not bother to do it manually here.
    • I have used the following app to generate json-model-access code: https://itunes.apple.com/de/app/json-accelerator/id511324989?mt=12 (currently free) with prefix IMGA
    • I have written a nice category using method swizzling in order to not change running code (and hopefully removing my code very soon):
    • (所有设备和回退机制的实现都没有完成!!)

      (implementation not complete for all devices and fallback mechanisms!!)

      #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
      

      这篇关于使用没有imageNamed的xcassets来防止内存问题?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆