iphone - 在小框架中显示大图像 UIImageView 仍然消耗大量内存?

发布于 2024-11-25 08:15:01 字数 224 浏览 1 评论 0原文

我有几张大小约为 1.5 MB 的大图像。我使用 UIViewContentModeScaleFit 在 UIImageView 中显示它们中的每一个。

UIImageViews 的帧大小仅为 150 * 150。

我的问题是,

我明白如果我全屏显示大图像,内存会大幅增加。

但是如果它们在小的 UIImageView 中,它们仍然会消耗内存吗?

谢谢

I have several big images with size of about 1.5 MB. I display each of them in a UIImageView with UIViewContentModeScaleFit.

The frame size of the UIImageViews is just 150 * 150.

My question is

I understand if I display the big images for full screens, the memory will go up tremendously.

But if they are in small UIImageView, will they still cost memory?

Thanks

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

冧九 2024-12-02 08:15:01

UIImageUIImageView 是不同的东西。每个 UIImageView 都知道显示其关联的 UIImage 的尺寸。 UIImage 没有如何显示自身或如何用于显示的概念,因此仅更改 UIImageView 的大小这一行为不会对UIImage。因此它对总内存使用没有影响。

您可能想要做的是使用 Core Graphics 获取 UIImage 并生成它的 150x150 版本作为新的 UIImage,然后将其推送到 UIImageView.

要执行缩放,类似下面的代码(在我输入时编写,因此没有彻底检查)应该可以完成这项工作:

#include <math.h>

- (UIImage *)scaledImageForImage:(UIImage *)srcImage toSize:(CGSize)maxSize
{
    // pick the target dimensions, as though applying
    // UIViewContentModeScaleAspectFit; seed some values first
    CGSize sizeOfImage = [srcImage size];
    CGSize targetSize; // to store the output size

    // logic here: we're going to scale so as to apply some multiplier
    // to both the width and height of the input image. That multiplier
    // is either going to make the source width fill the output width or
    // it's going to make the source height fill the output height. Of the
    // two possibilities, we want the smaller one, since the larger will
    // make the other axis too large
    if(maxSize.width / sizeOfImage.width < maxSize.height / sizeOfImage.height)
    {
        // we'll letter box then; scaling width to fill width, since
        // that's the smaller scale of the two possibilities
        targetSize.width = maxSize.width;

        // height is the original height adjusted proportionally
        // to match the proportional adjustment in width
        targetSize.height = 
                      (maxSize.width / sizeOfImage.width) * sizeOfImage.height;
    }
    else
    {
        // basically the same as the above, except that we pillar box
        targetSize.height = maxSize.height;
        targetSize.width = 
                     (maxSize.height / sizeOfImage.height) * sizeOfImage.width;
    }

    // images can be integral sizes only, so round up
    // the target size and width, then construct a target
    // rect that centres the output image within that size;
    // this all ensures sub-pixel accuracy
    CGRect targetRect;

    // store the original target size and round up the original
    targetRect.size = targetSize;
    targetSize.width = ceilf(targetSize.width);
    targetSize.height = ceilf(targetSize.height);

    // work out how to centre the source image within the integral-sized
    // output image
    targetRect.origin.x = (targetSize.width - targetRect.size.height) * 0.5f;
    targetRect.origin.y = (targetSize.height - targetRect.size.width) * 0.5f;

    // now create a CGContext to draw to, draw the image to it suitably
    // scaled and positioned, and turn the thing into a UIImage

    // get a suitable CoreGraphics context to draw to, in RGBA;
    // I'm assuming iOS 4 or later here, to save some manual memory
    // management.
    CGColorSpaceRef colourSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef context  = CGBitmapContextCreate(
           NULL,
           targetSize.width, targetSize.height,
           8, targetSize.width * 4,
           colourSpace, 
           kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast);
    CGColorSpaceRelease(colourSpace);

    // clear the context, since it may currently contain anything.
    CGContextClearRect(context, 
                  CGRectMake(0.0f, 0.0f, targetSize.width, targetSize.height));

    // draw the given image to the newly created context
    CGContextDrawImage(context, targetRect, [srcImage CGImage]);

    // get an image from the CG context, wrapping it as a UIImage
    CGImageRef cgImage = CGBitmapContextCreateImage(context);
    UIImage *returnImage = [UIImage imageWithCGImage:cgImage];

    // clean up
    CGContextRelease(context);
    CGImageRelease(cgImage);

    return returnImage;
}

显然,我通过大量注释使其看起来很复杂,但实际上只有 23 行。

UIImages and UIImageViews are separate things. Each UIImageView knows the size at which to display its associated UIImage. A UIImage has no concept of how to display itself or how it'll be used for display, so the mere act of changing the size of a UIImageView has no effect on the UIImage. Hence it has no effect on total memory usage.

What you probably want to do is use Core Graphics to take a UIImage and produce 150x150 version of it as a new UIImage, then push that into a UIImageView.

To perform the scale, code something like the following (written as I type, so not thoroughly checked) should do the job:

#include <math.h>

- (UIImage *)scaledImageForImage:(UIImage *)srcImage toSize:(CGSize)maxSize
{
    // pick the target dimensions, as though applying
    // UIViewContentModeScaleAspectFit; seed some values first
    CGSize sizeOfImage = [srcImage size];
    CGSize targetSize; // to store the output size

    // logic here: we're going to scale so as to apply some multiplier
    // to both the width and height of the input image. That multiplier
    // is either going to make the source width fill the output width or
    // it's going to make the source height fill the output height. Of the
    // two possibilities, we want the smaller one, since the larger will
    // make the other axis too large
    if(maxSize.width / sizeOfImage.width < maxSize.height / sizeOfImage.height)
    {
        // we'll letter box then; scaling width to fill width, since
        // that's the smaller scale of the two possibilities
        targetSize.width = maxSize.width;

        // height is the original height adjusted proportionally
        // to match the proportional adjustment in width
        targetSize.height = 
                      (maxSize.width / sizeOfImage.width) * sizeOfImage.height;
    }
    else
    {
        // basically the same as the above, except that we pillar box
        targetSize.height = maxSize.height;
        targetSize.width = 
                     (maxSize.height / sizeOfImage.height) * sizeOfImage.width;
    }

    // images can be integral sizes only, so round up
    // the target size and width, then construct a target
    // rect that centres the output image within that size;
    // this all ensures sub-pixel accuracy
    CGRect targetRect;

    // store the original target size and round up the original
    targetRect.size = targetSize;
    targetSize.width = ceilf(targetSize.width);
    targetSize.height = ceilf(targetSize.height);

    // work out how to centre the source image within the integral-sized
    // output image
    targetRect.origin.x = (targetSize.width - targetRect.size.height) * 0.5f;
    targetRect.origin.y = (targetSize.height - targetRect.size.width) * 0.5f;

    // now create a CGContext to draw to, draw the image to it suitably
    // scaled and positioned, and turn the thing into a UIImage

    // get a suitable CoreGraphics context to draw to, in RGBA;
    // I'm assuming iOS 4 or later here, to save some manual memory
    // management.
    CGColorSpaceRef colourSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef context  = CGBitmapContextCreate(
           NULL,
           targetSize.width, targetSize.height,
           8, targetSize.width * 4,
           colourSpace, 
           kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast);
    CGColorSpaceRelease(colourSpace);

    // clear the context, since it may currently contain anything.
    CGContextClearRect(context, 
                  CGRectMake(0.0f, 0.0f, targetSize.width, targetSize.height));

    // draw the given image to the newly created context
    CGContextDrawImage(context, targetRect, [srcImage CGImage]);

    // get an image from the CG context, wrapping it as a UIImage
    CGImageRef cgImage = CGBitmapContextCreateImage(context);
    UIImage *returnImage = [UIImage imageWithCGImage:cgImage];

    // clean up
    CGContextRelease(context);
    CGImageRelease(cgImage);

    return returnImage;
}

Obviously I've made that look complicated by commenting it very heavily, but it's actually only 23 lines.

最冷一天 2024-12-02 08:15:01

它们在帧缓冲内存(即保存用于显示的像素的内存)方面的成本不会很高,但在内存中保存未缩放的图像仍然会产生相当大的成本。如果1.5MB是压缩后的大小。这可能(iOS 使用每个图像大约 4 字节 * 宽度 * 高度来存储未压缩的 UIImage)。当您的应用程序进入后台时,此类图像与内核将执行的自动内存管理的交互也很差 - 当出现内存压力时,后备存储会被释放,但后备图像不会。

确定这是否是一个问题(以及您是否应该自己调整它们的大小、存储较小的版本并发布较大的版本)的最佳方法是使用 VM Tracker 通过 Instruments 运行您的应用程序。如果存储图像的区域过多,您将能够对其进行诊断,并选择适当的解决方案。您可能想查看有关 iOS 内存管理的 WWDC 2011 会议,其中详细介绍了图像内存使用情况,包括使用 Instruments 查找问题。

与往常一样,在优化之前先进行配置文件(或者,如苹果所说,仪器)!

They won't cost as much in terms of frame buffer memory (i.e. memory holding the pixels for display), but holding the unscaled images in memory will still incur a sizeable cost. If 1.5MB is the compressed size. that's likely to be high (iOS uses approximately 4 bytes * Width * Height per image to store uncompressed UIImages). Such images also interact poorly with the automatic memory management the kernel will perform when your app goes into the background - backing stores are released when memory pressure occurs, but the backing image isn't.

The best way to work out if this is a problem (and if you should resize them yourself, store the smaller versions, and release the large ones) is to run your app through Instruments using the VM Tracker. If the regions storing the images are excessive, you'll be able to diagnose them, and pick the appropriate solution. You might like to view the WWDC 2011 session on iOS Memory Management, which goes into image memory usage in some detail, including using Instruments to find problems.

As always, profile (or, as Apple might say, Instrument) before you optimise!

十二 2024-12-02 08:15:01
#import <ImageIO/ImageIO.h>
#import <MobileCoreServices/MobileCoreServices.h>

+ (UIImage *)resizeImage:(UIImage *)image toResolution:(int)resolution {
  NSData *imageData = UIImagePNGRepresentation(image);
  CGImageSourceRef src = CGImageSourceCreateWithData((__bridge CFDataRef)imageData, NULL);
  CFDictionaryRef options = (__bridge CFDictionaryRef) @{
                                                   (id) kCGImageSourceCreateThumbnailWithTransform : @YES,
                                                   (id) kCGImageSourceCreateThumbnailFromImageAlways : @YES,
                                                   (id) kCGImageSourceThumbnailMaxPixelSize : @(resolution)
                                                   };
  CGImageRef thumbnail = CGImageSourceCreateThumbnailAtIndex(src, 0, options);
  CFRelease(src);
  UIImage *img = [[UIImage alloc]initWithCGImage:thumbnail];
  return img;
}
#import <ImageIO/ImageIO.h>
#import <MobileCoreServices/MobileCoreServices.h>

+ (UIImage *)resizeImage:(UIImage *)image toResolution:(int)resolution {
  NSData *imageData = UIImagePNGRepresentation(image);
  CGImageSourceRef src = CGImageSourceCreateWithData((__bridge CFDataRef)imageData, NULL);
  CFDictionaryRef options = (__bridge CFDictionaryRef) @{
                                                   (id) kCGImageSourceCreateThumbnailWithTransform : @YES,
                                                   (id) kCGImageSourceCreateThumbnailFromImageAlways : @YES,
                                                   (id) kCGImageSourceThumbnailMaxPixelSize : @(resolution)
                                                   };
  CGImageRef thumbnail = CGImageSourceCreateThumbnailAtIndex(src, 0, options);
  CFRelease(src);
  UIImage *img = [[UIImage alloc]initWithCGImage:thumbnail];
  return img;
}
韵柒 2024-12-02 08:15:01

是的。您应该创建图像的缩小版本,将它们缓存到磁盘上的某个位置,在图像视图中使用小版本,并在可能的情况下卸载大版本。

Yep. You should create downsized versions of the images, cache them to disk somewhere, use the small versions in your image views and unload the big ones if you can.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文