我目前正在开发一个 iPad 应用程序,它将 ~250 个 UIImage
s 加载到 UIButton
s 中(然后更改颜色),创建一个世界地图 - 每个国家有自己的按钮和相应的图像 - 加载游戏时。我遇到的问题是,在视网膜 iPad 上,应用程序在加载图像时使用了大约 650MB 的 RAM,这太疯狂了。
当应用最初加载游戏时,它使用以下代码将图像设置为按钮(Territory
是 UIButton
的子类)。
//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
正在使用内存:
该屏幕截图是在领土的图像文件中具有原始颜色时拍摄的。我正在使用 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 raster data
正在使用内存。
不知道为什么使用内存的东西不一样。
我 posted a thread在 Apple Developer Forums 上,这也是该问题的前奏。
我还联系了 Apple Developer TSI,他们建议使用“延迟图像加载”。我对此进行了调查,但我还没有找到在非基于页面的 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)scrollViewDidScrollUIScrollView *)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 没有设置为分页,试试这个。
让我的 View Controller 成为 ScrollView 的代表(如果你在代码中这样做,你必须修改你的 View Controller 的 .h 来说明它符合 UIScrollViewDelegate)。
定义一个scrollViewDidScroll方法,(a)确定 ScrollView 可见部分的框架; (b) 确定哪些 subview 与该可见部分相交; (c) 加载可见的项目,卸载不可见的项目。
它最终会看起来像这样。
- (void)scrollViewDidScrollUIScrollView *)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];
}
}
}
关于ios - 应用程序分配 650MB 的 RAM 到 CG 光栅数据加载 ~250 UIImages,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22952027/
欢迎光临 OStack程序员社区-中国程序员成长平台 (https://ostack.cn/) | Powered by Discuz! X3.4 |