ios - 应用程序分配 650MB 的 RAM 以加载 CG 光栅数据 ~250 个 UIImages

标签 ios objective-c xcode uiimage

我目前正在开发一个 iPad 应用程序,它将大约 250 个 UIImage 加载到 UIButton 中(然后更改颜色),创建世界地图 - 每个国家有自己的按钮和相应的图像 - 在加载游戏时。我遇到的问题是,在视网膜 iPad 上,应用程序在加载图像时使用了 ~650MB 的 RAM,这太疯狂了。

当应用最初加载游戏时,它使用以下代码将图像设置到按钮(TerritoryUIButton 的子类)。

//Initialize the arrays of each territory and add an action to the territory
    for (int i = (int)territoryArray.count - 1; i >= 0; i--) {

        @autoreleasepool {

            //Cast the object into a territory object
            Territory *ter = (Territory *)[territoryArray objectAtIndex:i];

            //Set the territory's image
            [ter setImage:[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:@"%@T%i.png", [defaults objectForKey:@"Current_Map"], i + 1] ofType:nil]] forState:UIControlStateNormal];
        }
    }

这会导致以下内存使用情况。尖峰在循环运行时出现,但即使该 block 被 @autoreleasepool 包围也不会消失。请注意,ImageIO 正在使用内存:

Without coloring

该屏幕截图是在各地区的图像文件中具有原始颜色时截取的。我正在使用 MGImageUtilities 中的 UIImage+Tint 库为图像着色。使用该库时,将使用以下代码:

[ter setImage:[[ter imageForState:UIControlStateNormal] imageTintedWithColor:[UIColor colorWithRed:[[colors objectAtIndex:0] floatValue]/255.0 green:[[colors objectAtIndex:1] floatValue]/255.0 blue:[[colors objectAtIndex:2] floatValue]/255.0 alpha:1.0]] forState:UIControlStateNormal];

在加载所有图像后,在另一个循环(包含在另一个函数中的 @autoreleasepool 中)使用此代码时,会发生以下内存使用情况。请注意,CG 光栅数据 正在使用内存。

With coloring

我不知道为什么使用内存的东西不一样。

posted a thread在 Apple Developer Forums 上也是这个问题的前奏。

我也联系过 Apple Developer TSI,他们推荐使用“lazy image loading”。我对此进行了调查,但还没有找到在非基于页面的 UIScrollView 上执行此操作的方法。

在这一点上,我几乎不知道如何解决这个问题。在数小时的时间里,我已经尝试了所有我能想到的方法来解决这个问题,但我似乎无法想出任何可行的解决方案。如果有人可以帮助我,我将不胜感激。如果您想查看更多信息或代码,请告诉我,我很乐意发布。预先感谢您的帮助。

编辑:

我一直在试验,我正在使用 scrollViewDidScroll: 中的以下代码。它似乎在工作,但内存没有被释放,它继续上升。想法?

CGRect currentF = [scrollView convertRect:scrollView.bounds toView:scrollView.contentView];

    //Loop through all territories and determine which need to be rendered
    for (int i = (int)territoryArray.count - 1; i >= 0; i--) {

        @autoreleasepool {

            //See if the territory is on-screen
            if (CGRectIntersectsRect(currentF, [[territoryArray objectAtIndex:i] frame])) {

                //See if the territory needs an image
                if ([[territoryArray objectAtIndex:i] image] == nil) {

                    //Set the territory's image
                    [[territoryArray objectAtIndex:i] setImage:[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:@"%@T%i.png", [defaults objectForKey:@"Current_Map"], i + 1] ofType:nil]] forState:UIControlStateNormal];
                }
            } else {
                //Remove the territory's image, it is off-screen
                [[territoryArray objectAtIndex:i] setImage:nil forState:UIControlStateNormal];
            }
        }
    }

最佳答案

我发现,当我必须将图像加载到 ScrollView 中时,为了避免内存问题和应用程序的一般响应,您可以增量加载图像。这意味着,尽管 ScrollView 中可能有 250 张图像,但您无法同时看到它们。所以加载他可见的瓷砖,以及它下面的几行。您可以在滚动时加载其余内容。

编辑:这是一个例子。

-(void)scrollViewDidScroll:(UIScrollView *)myScrollView {

int currentPage = (1 + myScrollView.contentOffset.x / kXItemSpacingIphone);
for (ItemView* itemView in [self.itemRow subviews]){
    if (itemView.tag >= currentPage-1 && itemView.tag <= currentPage+1)
    {
        //keep it visible
        if (!itemView.isLoaded) {
            [itemView layoutWithData:[self.items objectAtIndex:itemView.tag-1]];
        }
    }
    else
    {
        //hide it
        if (itemView.isLoaded) {
            [itemView unloadData];
        }

    }
}

上面的代码将加载可见页面上方页面和下方页面的图像。我发现页面滚动有点不稳定。但我正在从网络加载图像,因此您可能会发现这工作得很好。

祝你好运!

编辑 2:

如果您的 ScrollView 没有设置分页,试试这个。

  1. 使我的 View Controller 成为 ScrollView 的委托(delegate)(如果您在代码中执行此操作,则必须修改 View Controller 的 .h 以使其符合 UIScrollViewDelegate)。

  2. 定义一个 scrollViewDidScroll 方法,(a) 确定 ScrollView 可见部分的框架; (b) 确定哪个 subview 与该可见部分相交; (c) 加载可见的项目,卸载不可见的项目。

它最终会看起来像这样。

    - (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    // Determine the frame of the visible portion of the scrollview.

    CGRect visibleScrollViewFrame = scrollView.bounds;
    visibleScrollViewFrame.origin = scrollView.contentOffset;

    // Now iterate through the various items, remove the ones that are not visible,
    // and show the ones that are.

    for (Item *itemObject in self.itemCollection)
    {
        // Determine the frame within the scrollview that the object does (or 
        // should) occupy.

        CGRect itemObjectFrame = [self getItemObjectFrame:itemObject];

        // see if those two frames intersect

        if (CGRectIntersectsRect(visibleScrollViewFrame, itemObjectFrame))
        {
            // If it's visible, then load it (if it's not already).
            // Personally, I have my object have a boolean property that
            // tells me whether it's loaded or not. You can do this any
            // way you want.

            if (!itemObject.loaded)
                [itemObject loadItem];
        }
        else
        {
            // If not, go ahead and unload it (if it's loaded) to conserve memory.

            if (itemObject.loaded)
                [itemObject unloadItem];
        }
    }
}

Reference

关于ios - 应用程序分配 650MB 的 RAM 以加载 CG 光栅数据 ~250 个 UIImages,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22952027/

相关文章:

objective-c - 如何在 .h 文件中写入具有多个参数的方法的名称

ios - swift Xcode。从头开始重建应用程序并尝试复制以前版本的 coredata 模型

ios - 如何在控制台中查看 DDLogVerbose()

ios - 我如何知道 handleOpenURL 响应的文档类型?

ios - 裁剪放置图片实现电池电量效果

objective-c - 以编程方式从 .kext 目录获取 KEXT 的 bundle 标识符

ios - 更改单元格的背景颜色

ios - 在原型(prototype)单元格中填充文本字段

iOS Playground 不显示 UI 预览

ios - Swift:过滤数组中的每 4 个项目