Vous demandez:
Est-il possible que dealloc
soit appelé sur un objet dont le compteur de rétention n'est PAS égal à zéro?
Comme vous utilisez ARC, nous n'utilisons plus le "compteur de rétention" dans ce contexte. Mais en réponse à votre question, non, un objet ne peut pas être désalloué tant qu'il y a des références fortes. Et lorsque vous appelez scheduledTimerWithTimeInterval
, si c'est un minuteur répétitif, il maintiendra une référence forte à la cible
, empêchant la cible d'être désallouée (du moins jusqu'à ce que l'invalidation du minuteur soit appelée).
Considérez:
@implementation SecondViewController
- (void)viewDidLoad {
[super viewDidLoad];
[NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:@selector(logRetainCount) userInfo:nil repeats:YES];
}
- (void)logRetainCount {
NSLog(@"propres compteur de rétention: %ld", CFGetRetainCount((__bridge CFTypeRef)self));
}
- (void)dealloc {
NSLog("contrôleur de vue désalloué");
}
@end
Lorsque je pousse ce contrôleur de vue, je vois ce qui suit sur la console:
2016-09-15 15:50:10.279 MyApp\[7777:159811\] propres compteur de rétention: 3
2016-09-15 15:50:13.340 MyApp\[7777:159811\] propres compteur de rétention: 3
Et lorsque je retire ce contrôleur de vue, je vois:
2016-09-15 15:50:16.338 MyApp\[7777:159811\] propres compteur de rétention: 2
2016-09-15 15:50:19.270 MyApp\[7777:159811\] propres compteur de rétention: 2
Remarquez, nous ne voyons pas apparaître "contrôleur de vue désalloué" dans la console.
Lorsque je clique sur le bouton "Graphique de mémoire de débogage" de Xcode 8, nous pouvons voir que le minuteur maintient toujours une référence forte à celui-ci:
Vous demandez:
- Pourquoi
dealloc
est-il appelé si le compteur de rétention de l'objet n'atteint jamais 0?
Cela ne peut pas être le cas. Donc, nous devons avoir plusieurs instances du contrôleur de vue impliquées ici, une avec le minuteur répétitif qui n'est pas désalloué, et une sans le minuteur, qui est désalloué lorsque sa dernière référence forte est résolue. Mais peu importe l'objet qui est la cible
du minuteur aura toujours une référence forte à celui-ci jusqu'à ce que le minuteur soit invalidé, et il ne sera pas désalloué tant que le minuteur n'aura pas son invalidate
appelé.
- Puisque
dealloc
est appelé, le compteur de rétention ne devrait-il pas être au moins égal à 1 puisque l'instance de NSTimer
le retient?
Non, tant que le minuteur répétitif est en cours, sa cible ne peut pas être désallouée. Nous devons parler de plusieurs instances du contrôleur de vue (ou, contrairement à l'exemple ci-dessus, la cible
du minuteur n'était pas le contrôleur de vue).
Il y a beaucoup de façons d'introduire accidentellement des instances supplémentaires d'un contrôleur de vue. Par exemple (quelque chose que j'ai vu ici sur Stack Overflow plus d'une fois), considérez que vous avez fait une transition entre deux contrôleurs de vue, et que vous aviez un prepareForSegue
qui faisait quelque chose comme:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
SecondViewController *secondViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"SecondViewController"];
secondViewController.property = @"foo";
}
Ceci est incorrect, car en plus du contrôleur de vue que la transition a instancié (le segue.destinationViewController
), le code prepareForSegue
ci-dessus créera une autre instance. Celle créée dans prepareForSegue
sera désallouée dès qu'elle tombera hors de la portée, mais celle créée par la transition ne sera pas désallouée en raison du minuteur répétitif créé dans viewDidLoad
.
Je ne suggère pas que vous ayez fait cela, mais cela illustre une façon possible d'obtenir le comportement que vous décrivez.
Mais, en bref, non, en ARC, un objet qui a encore des références fortes ne sera pas désalloué. Ce n'est que lorsque la dernière référence forte restante est supprimée qu'il sera désalloué. Le code dans votre question ne peut, à lui seul, produire le comportement que vous décrivez. Vous devez avoir affaire à une instance supplémentaire du contrôleur de vue ou quelque chose d'autre de curieux. Je pourrais vous suggérer de créer un exemple simple qui reproduit le problème que vous décrivez car le code dans votre question ne le fait pas. Il y a autre chose qui se passe.