2009-05-25 9 views
1

Je lisais la documentation de la pomme pour memory management, et j'ai rencontré quelque chose que je ne comprends pas. Fondamentalement, je ne comprends pas pourquoi on n'a pas besoin de conserver une variable d'instance à travers la méthode "getter". J'ai écrit ce petit programme pour voir ce qui se passerait. Je pensais qu'il y aurait un accident, mais il me manque évidemment quelque chose.Objectif C Confusion de la gestion de la mémoire

// main.m 
// Test 
// 


#import <Foundation/Foundation.h> 
#import "Test.h" 

int main(int argc, char *argv[]) 
{ 
    NSAutoreleasePool *p = [[NSAutoreleasePool alloc] init]; 

    //Initialize the test object 
    Test *t = [[Test alloc] init]; 

    //Set the value to 5 
    [t setMyNum:[NSNumber numberWithInt:5]]; 

    //Save a temp number that points to the original number 
    NSNumber *tempNum = [t myNum]; 

    //release old number and retain new 
    [t setMyNum:[NSNumber numberWithInt:7]]; 

    //Shouldn't this crash because tempNum is pointing to a deallocated NSNumber??? 
    NSLog(@"the number is %@",tempNum); 

    [p drain]; 
    return 0; 
} 

Est-ce que tempNum ne pointe pas vers un objet désalloué ??

Toute aide est appréciée.

EDIT

Voici le code dans les méthodes getter et setter

#import "Test.h" 


@implementation Test 
- (void)setMyNum:(NSNumber *)newNum { 
    [newNum retain]; 
    [myNum release]; 
    myNum = newNum; 
} 

-(NSNumber *)myNum { 
    return myNum; 
} 
@end 

Comme vous pouvez le voir, je fais appel à la libération l'ancien objet.

EDIT

Il a été suggéré, et j'ai pensé à juste titre que la raison pour laquelle le tempNum est encore autour est parce qu'il n'avait pas encore été libéré de la piscine pour le moment. Mais même après avoir déplacé le [drain de la piscine] juste avant le message NSLog, il n'y a pas de crash ??? Bizarre.

Répondre

5

Étant donné que vous ne libérez aucun objet explicitement, rien n'est désaffecté jusqu'à ce que le pool autorelease soit autorisé à s'évacuer. Essayez d'insérer [p drain] avant le dernier appel NSLog. Il devrait planter l'appel NSLog. En outre, si vous ne retenez pas NSNumber dans votre méthode setMyNum:, vous constaterez qu'il se bloquera si vous ajoutez [p drain] avant que tempNum soit affecté.

Pour clarifier la question initiale, l'appel d'une méthode getter n'implique pas (et ne devrait pas nécessairement) que l'appelant souhaite prendre en charge (c'est-à-dire conserver) la variable. Si tel était le cas, ce code fuirait:

NSLog("Number is %@", [t myNum]); 

En outre, il semble que NSNumber a une optimisation pour laquelle un petit nombre, ils cache les objets NSNumber, conserver une copie supplémentaire, et retourner cette version. Donc pour les petites constantes, [NSNumber numberWithInt: N] retournera un objet avec 2 comptes de référence (disponible via [theNumber retainCount]). Pour voir explicitement ce qui se passe, utilisez une plus grande constante dans le programme, un NSNumber conservera un objet «frais» avec un nombre de références de 1 (qui sera également auto-libéré).

+0

Mais dans mon setMyNum, j'appelle explicitement «retain and release». Qu'arrive-t-il à l'ancien numéro après avoir appelé la libération? La libération – esiegel

+1

ne libère pas l'objet, il en soustrait simplement un au "nombre de retain" de cet objet. c'est seulement quand le nombre de retenue frappe 0 que l'objet est désalloué. Dans votre cas, le nombre (NSNumber représentant 5) a un nombre de retenues de 1 immédiatement après sa création, puis est incrémenté à 2 lorsqu'il est défini dans votre objet Test, puis est réduit à 1 lorsqu'un autre numéro NSN (représentant 7) est obtenu défini dans l'objet Test. – harms

+0

Je ne pense pas que ce soit totalement correct: J'ai essayé d'insérer un [p drain] avant que l'appel NSLog attend un crash, mais pas de crash ??? Il doit y avoir quelque chose d'autre qui retient la valeur? Encore confus – esiegel

1
#import "Test.h" 

@implementation Test 

- (void)setMyNum:(NSNumber *)newNum 
{  
    [newNum retain]; 
    [myNum release]; 
    myNum = newNum; 
} 

-(NSNumber *)myNum 
{  
    return myNum; 
} 

@end 

Ici, dans la méthode setter [myNum release] qui libère le mynum, mais nous donnons encore une nouvelle valeur qui est newnum, donc de la méthode de lecture du numéro temporaire obtient le numéro qui n'a pas été désallouée jusqu'à ce que le [p drain] donc il n'y aura pas de crash.

0
#import "Test.h" 

@implementation Test 

(void)setMyNum:(NSNumber *)newNum 
{  

[newNum retain]; 
[myNum release]; 
myNum = newNum; 

} 

(NSNumber *)myNum 
{  

return myNum; 

} 

@end 

Voici la méthode setter [myNum release]; qui libère le myNum, mais nous donnons encore une nouvelle valeur qui est newNum, donc de la méthode de lecture du numéro temporaire obtient le numéro qui n'a pas été désallouée jusqu'à ce que le [p drain]; donc il n'y aura pas de crash. Même si le code suivant ne tombe pas en panne car il existe un pool autorelease mais pas de méthode de libération automatique.

[t setMyNum:[NSNumber numberWithInt:70]]; 

La libération du pool ne libère pas le numéro.

+1

Ne pas poster quelque chose deux fois; éditez votre réponse si vous devez apporter des modifications. Veuillez également marquer les échantillons du programme en tant que tels et utiliser l'orthographe standard --- c'est-à-dire, pas tous les bouchons. –

Questions connexes