我会保持简短和明显:我有一个正在调用其 dealloc 方法的对象。我还每 3 秒调用一次 NSTimer 来记录以控制台所述对象的当前保留计数。
需要明确的是:我知道 NSTimer 将保留该对象。即使考虑到这一点,情况仍然不合理。
无论如何 - 当这个计时器触发时,对象的保留计数被记录为 3。这让我困惑有两个原因:
- 如果对象的保留计数从未达到 0,为什么要调用 dealloc?
- 既然 dealloc 被调用,那么保留计数至少不应该为 1,因为 NSTimer 实例正在保留它?
非常感谢任何帮助。谢谢。
编辑:代码:
[NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:@selector(logRetainCount) userInfo:nil repeats:YES];
^在viewDidLoad中设置
- (void)logRetainCount
{
NSLog(@"own retain count: %ld", CFGetRetainCount((__bridge CFTypeRef)self));
}
^记录保留计数的方法
- (void)dealloc {
NSLog(@"view controller deallocated");
}
^ VC 中实现的 dealloc 方法应该被释放
控制台输出:
own retain count: 5
view controller deallocated
own retain count: 3
最佳答案
你问:
Is it possible for
dealloc
to be called on an object whose retain count is NOT zero?
由于您使用的是 ARC,我们不再在该上下文中使用“保留计数”。但回答你的问题时,不,当存在强引用时,对象不能被释放。当您调用scheduledTimerWithTimeInterval
时,如果这是一个重复计时器,它将保持对 target
的强引用。 ,防止目标被释放(至少在调用计时器的 invalidate
之前)。
考虑:
@implementation SecondViewController
- (void)viewDidLoad {
[super viewDidLoad];
[NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:@selector(logRetainCount) userInfo:nil repeats:YES];
}
- (void)logRetainCount {
NSLog(@"own retain count: %ld", CFGetRetainCount((__bridge CFTypeRef)self));
}
- (void)dealloc {
NSLog(@"view controller deallocated");
}
@end
当我推送到此 View Controller 时,我在控制台上看到以下内容:
2016-09-15 15:50:10.279 MyApp[7777:159811] own retain count: 3 2016-09-15 15:50:13.340 MyApp[7777:159811] own retain count: 3
And when I pop that view controller off, I see:
2016-09-15 15:50:16.338 MyApp[7777:159811] own retain count: 2 2016-09-15 15:50:19.270 MyApp[7777:159811] own retain count: 2
Note, we do not see "view controller deallocated" appear in the console.
When I click on Xcode 8's "Debug Memory Graph" button, we can see that the timer is still keeping a strong reference to it:
You ask:
- Why is
dealloc
being called if the object's retain count is not ever reaching 0?
It can't be. So, we must have multiple instances of the view controller involved here, one that has the repeating timer that isn't deallocated, and one without the timer, that is deallocated when its last strong reference is resolved. But whatever object is the target
of the timer will still have a strong reference to it until the timer is invalidated, and it will not be deallocated until the timer has its invalidate
called.
- Since
dealloc
is getting called, shouldn't, at the very least, the retain count be 1 since theNSTimer
instance is holding onto it?
No, while the repeating timer is firing, its target cannot be deallocated. We must be talking about multiple instances of the view controller (or, unlike the example above, the target
of the timer wasn't the view controller).
There are lots of ways to accidentally introduce additional instances of a view controller. For a random example (one that I've seen here on Stack Overflow more than once), consider that you did a segue between two view controllers, and had a prepareForSegue
that did something like:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
SecondViewController *secondViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"SecondViewController"];
secondViewController.property = @"foo";
}
这是不正确的,因为除了 segue 实例化的 View Controller ( segue.destinationViewController
)之外,上面的 prepareForSegue
将创建另一个实例。创建于 prepareForSegue
一旦超出范围就会被释放,但是由 segue 创建的定时器不会被释放,因为 viewDidLoad
中创建了重复计时器。 .
我并不是说这就是您所做的,但它说明了获得您所描述的行为的一种可能方法。
但是,简而言之,不,在 ARC 中,仍然具有强引用的对象将不会被释放。只有当最后一个剩余的强引用被删除时,它才会被释放。您问题中的代码不能单独产生您所描述的行为。您必须处理 View Controller 的一些附加实例或类似的奇怪的东西。我可能建议您创建一个 simple example这会重现您所描述的问题,因为您问题中的代码不会重现您所描述的问题。还有其他事情正在发生。
关于ios - 是否可以在保留计数不为零的对象上调用 dealloc?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39520663/