是否可以在保留计数不为零的对象上调用 dealloc?

Is it possible for dealloc to be called on an object whose retain count is NOT zero?

我将保持简短明了:我有一个正在调用其 dealloc 方法的对象。我还有一个 NSTimer 每 3 秒被调用一次以记录以控制所述对象的当前保留计数。

明确一点:我知道 NSTimer 会保留对象。这么一想,情况还是不对。

无论如何 - 在这个计时器触发时,对象的保留计数被记录为 3。这让我感到困惑有两个原因:

  1. 如果对象的保留计数从未达到 0,为什么要调用 dealloc?
  2. 既然 dealloc 被调用,至少,保留计数不应该是 1,因为 NSTimer 实例正在持有它?

非常感谢任何帮助。谢谢。

编辑:代码:

[NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:@selector(logRetainCount) userInfo:nil repeats:YES];

^ 在 vi​​ewDidLoad 中设置

- (void)logRetainCount
{
    NSLog(@"own retain count: %ld", CFGetRetainCount((__bridge CFTypeRef)self));
}

^ 日志保留计数的方法

- (void)dealloc {
    NSLog(@"view controller deallocated");
}

^ dealloc 方法在 VC 中实现,应该被释放

控制台输出:

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,我们不再在该上下文中使用 "retain count"。但是在回答你的问题时,不,在有强引用的情况下不能释放对象。当您调用 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

当我推送到这个视图控制器时,我在控制台上看到以下内容:

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

当我关闭视图控制器时,我看到:

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

请注意,我们不会看到 "view controller deallocated" 出现在控制台中。

当我点击 Xcode 8 的 "Debug Memory Graph" 按钮时,我们可以看到计时器仍然保持对它的强引用:

你问:

  1. Why is dealloc being called if the object's retain count is not ever reaching 0?

不可能。因此,我们必须在此处涉及多个视图控制器实例,一个具有未释放的重复计时器,另一个没有计时器,在其最后一个强引用被解析时被释放。但是,无论对象是定时器的 target,在定时器失效之前,它仍然具有对它的强引用,并且在定时器调用其 invalidate 之前,它不会被释放。

  1. Since dealloc is getting called, shouldn't, at the very least, the retain count be 1 since the NSTimer instance is holding onto it?

不,当重复计时器触发时,它的目标不能被释放。我们必须讨论视图控制器的多个实例(或者,与上面的示例不同,计时器的 target 不是视图控制器)。

有很多方法可以不小心引入视图控制器的额外实例。举一个随机的例子(我在 Stack Overflow 上不止一次看到过),假设你在两个视图控制器之间做了一个 segue,并且有一个 prepareForSegue 做了类似的事情:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    SecondViewController *secondViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"SecondViewController"];
    secondViewController.property = @"foo";
}

这是不正确的,因为除了 segue 实例化的视图控制器(segue.destinationViewController)之外,上面的 prepareForSegue 将创建另一个实例。在 prepareForSegue 中创建的那个一旦超出范围就会被释放,但是由 segue 创建的那个不会被释放,因为在 viewDidLoad.[=34 中创建了重复计时器=]

我并不是说这就是你所做的,但它说明了一种可能的方式来获得你所描述的行为。

但是,简而言之,不,在 ARC 中,一个仍然有任何强引用的对象将不会被释放。只有当最后一个剩余的强引用被移除时,它才会被释放。您问题中的代码不能单独产生您描述的行为。您必须处理一些额外的视图控制器实例或类似的好奇。我可能建议您创建一个 simple example 来重现您描述的问题,因为您问题中的代码没有。还有其他事情。