如何在没有内存泄漏的情况下在 ARC 中的 UIView 之间切换?
Posted
技术标签:
【中文标题】如何在没有内存泄漏的情况下在 ARC 中的 UIView 之间切换?【英文标题】:How to switch between UIViews in ARC without memory leaks? 【发布时间】:2012-08-28 21:33:08 【问题描述】:我使用带有 ARC 的 xcode 4.4。我在 ViewController.m 中动态创建了 UIViews:
UIView* myviews[10];
然后在 - (void)viewDidLoad 函数中,我用我需要的图片填充每个函数
myviews[viewIndex] = [[UIView alloc]initWithFrame:myrec];
UIImage *testImg;
UIImageView * testImgView = [[UIImageView alloc]init];
testImg = [UIImage imageNamed:[NSString stringWithFormat:@"imgarray%d.png", viewIndex];
[testImgView setImage:testImg];
[myviews[viewIndex] addsubView:testImgView];
viewindex++;
所以一切似乎都很好,当我想从一个视图跳转到另一个视图时,接下来我会使用两个按钮:
[self.view addSubview:views[viewIndex]];
CATransition *animation = [CATransition animation];
[animation setDelegate:self];
[animation setDuration:1.0f];
[animation setType:@"rippleEffect"];
[animation setSubtype:kCATransitionFromTop];
//[animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
[self.view.layer addAnimation:animation forKey:@"transitionViewAnimation"];
似乎没什么不好,但是当我在视图之间切换时,内存消耗会以惊人的速度增长。然后我会收到内存不足的警告,或者有时应用程序会崩溃。
我尝试使用 UIViewController 数组并在控制器之间切换:没有任何变化,内存不足警告是我最终的结果..
也许我需要以某种方式清理内存?但是怎么做? ARC不允许使用release等..
在添加新子视图之前我尝试过的最后一个(对不起,可能不是很专业)是这样的
NSArray *viewsToRemove = [self.view subviews];
for (UIView *views in viewsToRemove)
[views removeFromSuperview];
但这也无济于事。
编辑:我需要澄清一件事:在应用程序执行期间我需要每个 UIView,这意味着我不能在应用程序退出之前释放它。
EDIT2:对代码进行编辑,使其看起来更像真实代码。
【问题讨论】:
【参考方案1】:一些想法:
首先,复制的代码有问题,因为您从未创建过 testImgView。而不是
testImgView.image = testImg;
你需要
testImgView = [UIImageView viewWithImage:image];
其次,您不需要进入图层动画。 See this code instead...
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:1.0];
[UIView setAnimationTransition: 110 forView:view cache:NO]; //110 for ripple effect
[UIView commitAnimations];
第三,您可以尝试将所有视图添加到带有标签的 viewDidLoad 中,然后设置为可见。
【讨论】:
我在下面回答,是的,代码只是关于如何创建带有图像的 UIViews 的示例。您的标签提示是一个好主意,但在我的情况下,我仍然需要动画......它似乎没有从数组中选择我的视图,而是在每次我尝试添加它时创建它。这是我看到的唯一合乎逻辑的答案。但我必须让它发挥作用。 哦,[UIView setAnimationTransition: 110 forView:view cache:NO] 在 ios 5.1 中不起作用?!我刚试了一下,视图就变了,好像根本没有动画一样。 对不起,我忘记了 addSubViews 不是动画的。您需要改为操作 alpha。 嗯,是的,但不幸的是,这不是我需要的 :( 但我会尝试使用标签,在我看来是个好主意。但对我来说,无论是否我是否从 superview 中删除了以前的。旧的保留在内存中并导致泄漏.. 但是如果你只创建一次,那么保留多少次都没关系。您是否使用 Instruments 来告诉您泄漏了什么?特别是,如果您在 ViewDidLoad 中创建它们,那么每次父视图加载时,您都会再次创建它们。 Jason 的 dealloc 代码对于确保您不会泄露它们很重要。或者,如果您在整个应用程序生命周期中都需要它们,则将数组创建移动到 applicationdidFinishLoading【参考方案2】:为什么不使用 NSMutableArray 来存储视图?它更容易,我敢说,更安全。
而且,当您从超级视图中删除它们时,也将它们从数组中删除
作为一般规则,请记住将其存储到数组中会保留它,删除它会释放它,因此如果将其存储到数组中,然后将其添加为子视图,它将具有保留计数 +2。然后,您将从 superview 中删除它(保留计数 -1),但它仍然是正数,因为它仍在您的数组中。
【讨论】:
好的,但是如果我将来需要它并从阵列中删除它?那我如何再次访问它?【参考方案3】:如果每次发生过渡时情况都变得更糟,您可以尝试在从超级视图中移除动画时从图层中移除。
NSArray *viewsToRemove = [self.view subviews];
for (UIView *views in viewsToRemove)
[views removeFromSuperview];
[views.layer removeAllAnimations];
【讨论】:
【参考方案4】:当您使用这样的数组结构(我假设为 ivar)时,ARC 并不真正知道当包含对象的结构消失时它必须释放您的视图。当您清理时,例如在您的 -dealloc
方法中,您只需遍历数组并将每个元素设置为 nil,然后 ARC 就会知道释放该视图是可以的。另一种选择是使用Foundation
集合类型,例如NSMutableArray
来保存您的视图,这样您就不必记得清理它们了。
例如:
- (void)dealloc
for( int i = 0; i < 10; i++ )
myviews[i] = nil;
如果在您的应用程序的生命周期中,您将新视图分配到此数组中,没关系,ARC 会做正确的事情。只要确保您在-dealloc
中将它们排除在外。如果您知道以相同的方式完成了它们,也可以提前释放它们,即遍历数组并将 nil 分配给每个元素。
【讨论】:
Jason,感谢您的回答,但在应用程序执行期间我仍然需要这些视图...我的问题是 - 当我以上面显示的方式在视图之间切换时,我的内存消耗增加了每次我从一个视图跳到另一个视图.. @AndreiGolubev 然后内存消耗可能是由其他原因引起的。这 10 个视图有多大?是否有可能多次创建此控制器并留在内存中?您需要检查这些东西,因为如果您只有这 10 个简单的静态视图,那么在每个视图都被创建并插入视图层次结构至少一次之后,您不应该看到任何堆增长。 不,控制器/UIViews 创建一次并放入一个数组中,然后我只需通过 [self addsubView: myviews[viewIndex]]; 在数组成员之间切换。图片总共20Mb,我怀疑这可能是原因。可以吗? @AndreiGolubev 20MB 已解压?另外,我不认为这是你的问题。如果您问题中的代码是您的实际代码,那么您甚至从未创建图像视图,并且您从未将未创建的图像视图添加到您创建并存储在数组中的视图中。当您分配图像时,它是无操作的,因为testImgView
在 ARC 下将是 nil
,[testImgView setImage:testImg];
什么都不做。
@Jaison,不,这不是实际代码,只是为了提示问题,因为实际代码很大,但我可以向你保证,意思是一样的。第 2 部分和第 3 部分是实际代码 sn-ps。是的,图像大约 20mb 并且没有压缩 png。以上是关于如何在没有内存泄漏的情况下在 ARC 中的 UIView 之间切换?的主要内容,如果未能解决你的问题,请参考以下文章