2 votes

Est-il possible que dealloc soit appelé sur un objet dont le compte de rétention n'est PAS zéro?

Je vais faire court et simple : J'ai un objet dont la méthode de libération est appelée. J'ai également un NSTimer qui est appelé toutes les 3 secondes pour enregistrer dans la console le nombre de références actuel dudit objet.

Pour être clair : Je sais que le NSTimer va conserver l'objet. La situation ne colle toujours pas même en tenant compte de cela.

Quoi qu'il en soit - au moment où ce minuteur se déclenche, le nombre de références de l'objet est enregistré comme étant de 3. Cela me perturbe pour 2 raisons :

  1. Pourquoi la libération est-elle appelée si le nombre de références de l'objet n'atteint jamais 0 ?
  2. Puisque la libération est appelée, le nombre de références ne devrait-il pas être au moins de 1 puisque l'instance NSTimer le retient ?

Toute aide est grandement appréciée. Merci.

Édition : code :

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

^ défini dans viewDidLoad

- (void)logRetainCount
{
    NSLog(@"nombre de références propre : %ld", CFGetRetainCount((__bridge CFTypeRef)self));
}

^ méthode qui enregistre le nombre de références

- (void)dealloc {
    NSLog("contrôleur de vue libéré");
}

^ méthode de libération implémentée dans le VC qui est censé être libéré

Et la sortie de la console :

nombre de références propre : 5

contrôleur de vue libéré

nombre de références propre : 3

5voto

Rob Points 70987

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:

minuteur

Vous demandez:

  1. 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é.

  1. 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.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X