iphone - 在 UIScrollView 中使用相当大的图像的 UIImageViews 使用的问题 dealloc'ing 内存

标签 iphone objective-c cocoa-touch memory-management

我有一个很大的 UIScrollView,我在其中放置了 3-4 个相当大(320x1500 像素左右)的 UIImageView 图像 block 。我将这些 UIImageViews 添加到我的 NIB 文件内的 ScrollView 中。我的 Controller 上有一个 socket ,那就是 UIScrollView。我为此使用了一个属性(非原子的,保留的),并将其合成。

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

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

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

- (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];
}

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

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

最佳答案

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

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

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

当然,imageNamed: 更易于使用,因为它默认为包中的任何内容,而不必查找路径。但是,您可以通过以下方式获取包的路径:

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;

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

- (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;
}

关于iphone - 在 UIScrollView 中使用相当大的图像的 UIImageViews 使用的问题 dealloc'ing 内存,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/289360/

相关文章:

ios - 如何使用EventKit处理重复事件

ios - 如何在 iPhone 锁定屏幕上放置一个图标?

ios - 从应用委托(delegate)中检索 managedObjectContext 时崩溃

objective-c - ant+ivy/maven 等 objective-c 的构建和依赖工具?

ios - 仅调用前三个号码

iphone - 我如何在 xcode 4 中引用一个单独的项目?

ios - 在collectionView中加载图像

ios - "interfaceOrientation"在 iOS 8 中已弃用,如何更改此方法 Objective C

ios - 如何在 iOS 生成的操作表上收听操作

ios - Facebook 权限 - com.apple.accounts 错误 8