iOS 开发:为什么我的视图控制器的保留计数如此奇怪?
我正在深入研究 iOS 开发,正在构建一个基于导航的应用程序,该应用程序没有完全释放被推送到导航堆栈上的视图之一。这是有问题的,因为视图控制器永远不会被释放,因此每次将视图控制器推入堆栈时,它使用的内存都会累积。因此,在调查该问题后,我发现视图控制器的保留计数非常奇怪。一旦倒计时器达到零,相关视图控制器就会被推入堆栈。
这是在计时器回调中创建视图控制器、显示其保留计数并将其推送到导航堆栈的代码...
-(void)updateCountDownTimer //Defined in MyViewController_A class
{
[self setTimeRemaining:([self timeRemaining] - 1)];
[[self countDownLabel] setAlpha:1];
[[self countDownLabel] setText:[NSString stringWithFormat:@"%d", [self timeRemaining]]];
//Fade out the current time
[UIView beginAnimations:@"FadeAnimation" context:nil];
[UIView setAnimationDuration:1];
[[self countDownLabel] setAlpha:0];
[UIView commitAnimations];
if ([self timeRemaining] == 0)
{
MyViewController_B *myvc_b = [[MyViewController_B alloc] initWithNibName:@"MyView_B_iPhone" bundle:nil];
[[self navigationController] pushViewController:myvc_b animated:YES];
NSLog(@"updateCountDownTimer: %d", [myvc_b retainCount]);
[myvc_b release];
[[self countDownTimer] invalidate];
[[self countDownLabel] setHidden:YES];
}
}
这是按下暂停按钮后将视图控制器从导航堆栈弹出的代码...
- (void)pauseButtonPressed:(id)sender
{
//Stop the timer
[puzzleTimer invalidate];
NSLog(@"pauseButtonPressed before pop: %d", [self retainCount]);
//return to the previous view
[[self navigationController] popViewControllerAnimated:YES];
NSLog(@"pauseButtonPressed after pop: %d", [self retainCount]);
}
这是控制台输出,显示整个过程中非常奇怪的保留计数...
2010-12-02 17:50:38.062 MyApp[821:307] updateCountDownTimer: 5
2010-12-02 17:50:40.453 MyApp[821:307] pauseButtonPressed before pop: 2
2010-12-02 17:50:40.462 MyApp[821:307] pauseButtonPressed after pop: 4
我是 iOS 开发新手,但代码对我来说似乎非常简单,所以我不知道我错过了什么。
预先非常感谢您的智慧!
更新:看起来 Leaks 工具正在报告将前一个视图控制器推入堆栈的代码行(即负责推入堆栈的视图控制器)的代码行存在泄漏。有问题的视图控制器)。代码再次非常简单,所以我不知道为什么它报告泄漏...
MyViewController_A *myvc_a = [[MyViewController_A alloc] initWithNibName:@"MyView_A_iPhone" bundle:nil];
[[self navigationController] pushViewController:myvc_a animated:YES]; //<--Leak being reported here
[myvc_a release];
*更新:*发现问题,就像每个人都说的那样,并且与之前的问题相同如下面评论中发布的链接所示,我的活动对象仍在引用我的视图控制器,这阻止了它解除分配。就我而言,我有两个针对我的视图控制器的计时器,并且在我将视图从堆栈中弹出之前这些计时器并未失效,这意味着有两个活动对象仍在引用视图控制器。这是我在苹果文档中发现的一个片段,它发现了这个问题......
也许更重要的是,计时器还 保持对其的强烈引用 目标。这意味着只要 计时器仍然有效(否则你 正确遵守内存管理 规则),其目标不会是 解除分配。
不管怎样,再次感谢所有提供帮助的人!
I'm diving into iOS development and I'm building a navigation based app that wasn't fully releasing one of the views that was being pushed onto the nav stack. This is problematic because the view controller is never being deallocated, so the memory it uses just builds up every time the view controller is pushed on to the stack. So after investigating the issue, I found the retain counts for the view controller were really strange. The view controller in question is pushed on to the stack once a countdown timer reaches zero.
Here's the code that creates the view controller in the timer callback, displays its retain count, and pushes it onto the nav stack...
-(void)updateCountDownTimer //Defined in MyViewController_A class
{
[self setTimeRemaining:([self timeRemaining] - 1)];
[[self countDownLabel] setAlpha:1];
[[self countDownLabel] setText:[NSString stringWithFormat:@"%d", [self timeRemaining]]];
//Fade out the current time
[UIView beginAnimations:@"FadeAnimation" context:nil];
[UIView setAnimationDuration:1];
[[self countDownLabel] setAlpha:0];
[UIView commitAnimations];
if ([self timeRemaining] == 0)
{
MyViewController_B *myvc_b = [[MyViewController_B alloc] initWithNibName:@"MyView_B_iPhone" bundle:nil];
[[self navigationController] pushViewController:myvc_b animated:YES];
NSLog(@"updateCountDownTimer: %d", [myvc_b retainCount]);
[myvc_b release];
[[self countDownTimer] invalidate];
[[self countDownLabel] setHidden:YES];
}
}
and here's the code that pops the view controller off the nav stack once the pause button is pressed...
- (void)pauseButtonPressed:(id)sender
{
//Stop the timer
[puzzleTimer invalidate];
NSLog(@"pauseButtonPressed before pop: %d", [self retainCount]);
//return to the previous view
[[self navigationController] popViewControllerAnimated:YES];
NSLog(@"pauseButtonPressed after pop: %d", [self retainCount]);
}
and here's the console output that shows really strange retain counts throughout the process...
2010-12-02 17:50:38.062 MyApp[821:307] updateCountDownTimer: 5
2010-12-02 17:50:40.453 MyApp[821:307] pauseButtonPressed before pop: 2
2010-12-02 17:50:40.462 MyApp[821:307] pauseButtonPressed after pop: 4
I'm new to iOS development, but the code seems pretty straightforward to me, so I don't know what I'm missing.
Thanks so much in advance for your wisdom!
UPDATE: It looks like the Leaks instrument is reporting a leak on the line of code that pushes the previous view controller onto the stack (that is, the view controller responsible for pushing the view controller in question). The code is once again very straightforward, so I don't know why it's reporting a leak...
MyViewController_A *myvc_a = [[MyViewController_A alloc] initWithNibName:@"MyView_A_iPhone" bundle:nil];
[[self navigationController] pushViewController:myvc_a animated:YES]; //<--Leak being reported here
[myvc_a release];
*UPDATE:*Found the problem, it was just as everyone was saying and the same problem as was shown in the link posted in the comments below, I had live objects still referencing my view controller, which prevented it from deallocating. In my case, I had two timers that were targeting my view controller and those timers weren't being invalidated before I popped the view off the stack, which meant there were two live objects still referencing the view controller. Here's a snippet I found in the Apple docs which uncovered the problem...
Perhaps more importantly, a timer also
maintains a strong reference to its
target. This means that as long as a
timer remains valid (and you otherwise
properly abide by memory management
rules), its target will not be
deallocated.
Anyhow, thanks again to everyone who helped!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
你没有遗漏任何东西——
UINavigationController
的实例只是对内部的保留计数做了奇怪的事情。仅当您发现要尝试修补的特定内存泄漏时,才应该担心
retainCount
。当然,在这种情况下,您确实遇到了问题...retainCount
只是没有帮助,因为它太奇怪了。当您执行 pop 操作时,您可能会检查 MyViewController 上是否调用了 dealloc。另外,在测试之前,请注释掉检查保留计数的行。调用
retainCount
有时会添加到retainCount
。要真正确定发生了什么,在 Xcode 中,转到“运行”菜单,然后选择“使用性能工具运行”>“运行”。泄漏。按下并弹出该视图控制器,您应该会看到它作为泄漏弹出。您将能够看到对象上的所有
retain
和release
调用。如果您确实遇到困难,请参阅 Apple 的查找指南Leaks 有一些更聪明的解决方案。祝你好运!
You're not missing anything -- instances of
UINavigationController
just do strange, strange things to the retain count internally.You should only worry about the
retainCount
if you see a specific memory leak that you're trying to patch up. In this case, of course, you DO have a problem... theretainCount
just doesn't help, since it's so bizarre.You might check if dealloc is getting called on
MyViewController
when you do the pop. Also, before you test, comment out the lines that check the retainCount. CallingretainCount
will sometimes add to theretainCount
.To really nail down what's going on, in Xcode, go to the Run menu, and select Run With Performance Tool > Leaks. Push and pop that view controller, and you should see it pop up as a leak. You'll be able to see all of the
retain
andrelease
calls on the object.If you're really stuck, Apple's guide to Finding Leaks has a few more clever solutions. Good luck!
就内存管理而言,您的代码没有任何问题。
您不应该依赖保留计数来检查对象是否被正确释放,因为系统也会保留它需要的内容并在适当的时候释放。例如,当您将视图控制器添加到堆栈时,它会被导航控制器及其子视图保留,并且当它弹出时,它会发送一条发布消息来传播其所有子视图。
一般规则是,如果您分配、保留或复制一个对象,则您有责任释放它。其他一切都由系统处理,并将被自动释放池刷新。
There is nothing wrong with your code as far as memory management is concerned.
You should not rely on retain counts to check if your objects are being released properly as the system will also be retaining what it needs and releasing when appropriate. For instance when you add your view controller to the stack it gets retained by the nav controller along with its subviews and when it's popped out it gets send a release message which propagates all of its subviews.
The general rule is if you alloc, retain or copy an object, it is your responsibility to release it. Everything else is dealt with by the system and will be flushed with the auto release pool.
永远、永远、永远不要查看对象的保留计数。它们不应该以编程方式使用,并且在您尝试调试代码时会产生误导。
原因如下:您知道在代码中,您正在调用保留和释放来管理保留计数。但您也可能会调用 -autorelease,这会导致您的保留计数在以后减少,而您几乎无法控制。更糟糕的是,每当您将对对象的引用传递给您无法控制其实现的对象时(这可能发生在您创建的大多数对象上),接收对象可能会对保留计数进行自己的调整 -该对象可能会将您的对象传递给其他对象,这也会调整保留计数。
关键是,您不应该在任何时候出于任何原因查看对象的保留计数。它们只会让你感到困惑。您的工作是正确管理您自己对对象的声明,并相信其他人编写的代码也能这样做。
Never, ever, ever look at the retain counts of your objects. They should not be used programmatically, and are misleading when you are trying to debug your code.
Here's why: You know that in your code, you are making calls to retain and release to manage your retain count. But you may also be making calls to -autorelease, which causes your retain count to be decremented at a later date over which you have little or no control. Worse, any time you pass a reference to your object to an object that you do not control the implementation of (which is likely to happen to most of the objects you create), the receiving object may make its own adjustments to the retain count - and that object may pass your object to yet other objects, which also adjust the retain count.
The point is, you should not ever look at the retain count of your objects for any reason at any time. They will only serve to confuse you. Your job is to manage your own claims to an object correctly, and trust that the code written by others is doing so as well.