问题解除分配 UIImageViews 使用的内存在 UIScrollView 中具有相当大的图像 [英] Problem dealloc'ing memory used by UIImageViews with fairly large image in an UIScrollView

查看:22
本文介绍了问题解除分配 UIImageViews 使用的内存在 UIScrollView 中具有相当大的图像的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个很大的 UIScrollView,我在其中放置了 3-4 个相当大(320x1500 像素左右)的 UIImageView 图像块.我将这些 UIImageViews 添加到我的 NIB 文件内的滚动视图中.我的控制器上有一个出口,那就是 UIScrollView.我为此使用了一个属性(非原子的、保留的),并对其进行了合成.

I have a large UIScrollView into which I'm placing 3-4 rather large (320x1500 pixels or so) UIImageView image tiles. I'm adding these UIImageViews to the scroll view inside of my NIB files. I have one outlet on my controller, and that is to the UIScrollView. I'm using a property (nonatomic, retain) for this, and sythesizing it.

我的问题是:当我在内存监视器中观察到这一点时,我可以看到当加载所有这些图像的视图时(如预期的那样),使用的内存增加了很多.但是当我离开视图时,它和它的控制器被解除分配,但似乎并没有放弃它们所占用的内存附近的任何地方.当我将这些视图中的一个(我的应用程序中有几个)剪切为 1-3 张 320x460 的图像并保持其他所有内容相同时,它可以很好地重新获取内存.

My question is this: When I observe this in Memory Monitor, I can see that the memory used goes up quite a bit when the view with all these images is loaded (as expected). But when I leave the view, it and its controller are dealloc'd, but do not seem to give up anywhere near the memory they had taken up. When I cut one of these views (there are several in my app) down to just 1-3 images that were 320x460 and left everything else the same, it recaptures the memory just fine.

使用这么大的图片有什么问题吗?我在这段代码中做错了什么(粘贴在下面)?

Is there some issue with using images this large? Am I doing something wrong in this code (pasted below)?

这是导致问题的 viewController 的片段.

This is a snippet from the viewController that is causing problems.

- (CGFloat)findHeight 
{
    UIImageView *imageView = nil;
    NSArray *subviews = [self.scrollView subviews];

    CGFloat maxYLoc = 0;
    for (imageView in subviews)
    {
            if ([imageView isKindOfClass:[UIImageView class]])
            {
                    CGRect frame = imageView.frame;

                    if ((frame.origin.y + frame.size.height) > maxYLoc) {
                            maxYLoc  = frame.origin.y;
                            maxYLoc += frame.size.height;
                    }
            }
    }
    return maxYLoc;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    [self.scrollView setContentSize:CGSizeMake(320, [self findHeight])];

    [self.scrollView setCanCancelContentTouches:NO];
    self.scrollView.indicatorStyle = UIScrollViewIndicatorStyleWhite;
    self.scrollView.clipsToBounds = YES;                
    self.scrollView.scrollEnabled = YES;

    self.scrollView.pagingEnabled = NO;
}

- (void)dealloc {
    NSLog(@"DAY Controller Dealloc'd");
    self.scrollView = nil;
    [super dealloc];
}

更新:我注意到另一个奇怪的现象.如果我不使用视图上的滚动,它似乎挂在内存中.但是,如果我滚动一堆并确保所有 UIImageViews 都在某一时刻可见,它将释放并重新获得它丢失的大部分内存.

UPDATE: I've noticed another weird phenomenon. If I don't use the scroll on the view, it seems to be hanging on to the memory. But if I scroll around a bunch and ensure that all of the UIImageViews became visible at one point, it will free up and regain most of the memory it lost.

UPDATE2:我问这个的原因是我的应用程序实际上由于内存不足而崩溃.我不介意它是否只是缓存和使用额外的内存,但它似乎永远不会释放它 - 即使在 didReceiveMmoryWarning 条件下也是如此

UPDATE2: The reason I'm asking this is my app is actually crashing due to low memory. I wouldn't mind if it were just caching and using up extra memory, but it doesn't seem to ever release it - even in didReceiveMmoryWarning conditions

推荐答案

我已经解开了这个谜团 - 我很确定这是 Apple 方面的错误.

I've solved the mystery - and I'm pretty sure this is a bug on Apple's side.

正如 Kendall 所建议的(谢谢!),问题在于 InterfaceBuilder 如何从 NIB 文件加载图像.当您 initFromNib 时,所有 UIImageViews 将使用 UIImage 的 imageNamed: 方法使用 UIImage 进行初始化.此调用对图像使用缓存.通常,这就是您想要的.但是,对于非常大的图像以及滚动远离可见区域的图像,它似乎没有遵守内存警告并转储此缓存.这是我认为是 Apple 方面的错误(如果您同意/不同意,请发表评论 - 如果其他人同意,我想提交).正如我在上面所说的,如果用户滚动到足以使其全部可见,则这些图像使用的内存似乎确实被释放了.

As Kendall suggested (thanks!), the problem lies in how InterfaceBuilder loads images from the NIB file. When you initFromNib, all UIImageViews will init with a UIImage using the imageNamed: method of UIImage. This call uses caching for the image. Normally, this is what you want. However, with very large images and additionally ones that scroll far off of the visible area, it does not seem to be obeying memory warnings and dumping this cache. This is what I believe to be a bug on Apple's side (please comment if you agree/disagree - I'd like to submit this if others agree). As I said above, the memory used by these images does seem to be released if a user scrolls around enough to make it all visible.

我发现的解决方法(也是 Kendall 的建议)是将 NIB 文件中的图像名称留空.因此,您可以正常布置 UIImageView 元素,但不要选择图像.然后在您的 viewDidLoad 代码中,您进入并使用 imageWithContentsOfFile: 加载图像.此方法不会缓存图像,因此不会因保留大图像而导致任何内存问题.

The workaround that I've found (also Kendall's suggestion) is to leave the image name blank in the NIB file. So you lay out your UIImageView elements as normal, but don't select an image. Then in your viewDidLoad code, you go in and load an image using imageWithContentsOfFile: instead. This method does NOT cache the image, and therefore does not cause any memory issues with retaining large images.

当然,imageNamed: 更容易使用,因为它默认为bundle 中的任何内容,而不必查找路径.但是,您可以使用以下命令获取包的路径:

Of course, imageNamed: is a lot easier to use, because it defaults to anything in the bundle, rather than having to find the path. However, you can get the path to the bundle with the following:

NSString *fullpath = [[[NSBundle mainBundle] bundlePath];

综合起来,代码如下:

NSString *fullpath = [[[NSBundle mainBundle] bundlePath] stringByAppendingString:[NSString stringWithFormat:@"/%@-%d.png", self.nibName, imageView.tag]];
UIImage *loadImage = [UIImage imageWithContentsOfFile:fullpath];
imageView.image = loadImage;

因此将其添加到我上面的代码中,完整的函数如下所示:

So adding that to my code above, the full function looks like this:

- (CGFloat)findHeight 
{
    UIImageView *imageView = nil;
    NSArray *subviews = [self.scrollView subviews];

    CGFloat maxYLoc = 0;
    for (imageView in subviews)
    {
        if ([imageView isKindOfClass:[UIImageView class]])
        {
            CGRect frame = imageView.frame;

            if ((frame.origin.y + frame.size.height) > maxYLoc) {
                maxYLoc  = frame.origin.y;
                maxYLoc += frame.size.height;
            }

            NSString *fullpath = [[[NSBundle mainBundle] bundlePath] stringByAppendingString:[NSString stringWithFormat:@"/%@-%d.png", self.nibName, imageView.tag]];
            NSLog(fullpath);


            UIImage *loadImage = [UIImage imageWithContentsOfFile:fullpath];
            imageView.image = loadImage;
        }
    }
    return maxYLoc;
}

这篇关于问题解除分配 UIImageViews 使用的内存在 UIScrollView 中具有相当大的图像的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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