2010-02-07 4 views
0

Je construis une petite application iphone et utilise un singleton pour stocker et mettre à jour une chaîne qui est mise à jour lorsque l'utilisateur tape des lettres ou des chiffres sur l'écran pour former un code.Objectif C NSString étant publié dans singleton

dire qu'ils 3 puis appuyez sur S puis 4 et je dois suivre et combiner cette entrée pour me donner « 3S4 » dire. Lorsque le singleton est initialisé, il crée un NSString vide et j'utilise ensuite la méthode stringByAppendString pour l'ajouter à la prochaine lettre/numéro tapé. Quand je l'ai essayé pour la première fois, je n'avais pas la ligne [enterCode retain] et l'application plantait avec EXC_BAD_ACCESS, toujours après 2 entrées. J'ai mis la propriété NSZombie qui m'a dit que le code entré a été désaffecté mais je ne sais pas où et comment cela s'est passé. Tout ce que je sais, c'est qu'à la fin de la méthode addInput, le retainCount sera dit 2 puis directement après que je puisse voir (en appelant le singleton d'ailleurs) il descendra à 1 (quand la ligne de retenue est là).

Ma question est: si ce que je l'ai fait en ajoutant [enteredCode retain] travaille pour moi, je suis en train de battre des règles ici ou d'aller à ce sujet dans le mauvais sens/mauvais? Je ne peux juste pas voir pourquoi la chaîne est libérée.

Je suis nouveau à Objective-C BTW

dans MySingleton.h

@interface MySingleton : NSObject { 
    NSString *enteredCode; 
} 

dans MySingleton.m

-(void) addInput:(NSString *) input 
{ 
    NSLog(@"enteredCode retain count is : %d \n ",[enteredCode retainCount]); 

    enteredCode = [enteredCode stringByAppendingString:input]; 

NSLog(@"enteredCode retain count is : %d \n ",[enteredCode retainCount]); 

[enteredCode retain]; // without this the app crashes 

NSLog(@"enteredCode retain count is : %d \n ",[enteredCode retainCount]); 

} 

-(id) init 
{ 
    self = [super init]; 

    if (self) 
    { 
     enteredCode = @""; 


    } 

    return self; 
} 

Répondre

3

Tout d'abord, ne jamais utiliser la méthode -retainCount. Le nombre absolu de retenues sur un objet est un détail d'implémentation des structures et renvoie souvent des résultats confus.

Le nombre de retenues est quelque chose que vous devez maintenir entièrement sous la forme d'un ensemble équilibré de deltas. Si vous indiquez qu'un compte de conservation doit être ajouté à quelque chose, vous devez release ou autorelease cet objet quelque part. Fin de l'histoire.

Cette document explains it all.

Avec ces connaissances en main, la source de votre crash est une erreur de gestion de mémoire assez courante.

enteredCode = [enteredCode stringByAppendingString:input]; 

Chaque fois que cette ligne de code est exécuté, vous remplacez enteredCode par une instance autoreleased de NSString. Le pool autorelease est drainé et votre programme se bloque la prochaine fois que enteredCode est utilisé.

Votre solution de retenue enteredCode est seulement la moitié de la solution. Vous devez vous assurer que la valeur d'origine de enteredCode est également publiée. Voir les documents de gestion de la mémoire.

Si tel était mon application, je me tournerais enteredCode dans une @property qui copie la chaîne et toujours définir et accès enteredCode par cette propriété, ne conservant ou libérer manuellement dans mon code (en dehors de -dealloc, bien sûr).

+0

Un grand merci pour avoir éclairci ce point. Merci pour la réponse de tout le monde aussi. J'ai modifié le code maintenant pour définir enterCode comme @property. – Cerv

1

NSString stringByAppendingString: renvoie un nouveau NSString créé en ajoutant une chaîne à l'autre, et le nouveau NSString est défini sur autorelease, ce qui vide le pool autorelease et votre prochaine exécution plante l'application. Vous redéfinissez une chaîne existante avec stringByAppendingString: et cela provoque les problèmes de conservation. (Vous pouvez également utiliser NSMutableString et vous pouvez éviter cela.Soit dit en passant, vous pouvez faire if (self = [super init]) dans votre override init. La déclaration renvoie true si elle se produit ou peut se produire.

+0

Donc, ce que fait le questionneur original est correct, en conservant la chaîne après avoir créé une nouvelle instance. En ce qui concerne (self = [super init]), ce que fait l'affiche originale semble être la direction vers laquelle se dirige Apple. Si vous utilisez la macro "Create Init Method" dans XCode "self = [super init], if (self) ..." est ce qui est produit. –

1

Voilà comment votre code devrait ressembler:

@interface MySingleton : NSObject { 
    NSString *enteredCode; 
} 

@property (nonatomic, retain) NSString *enteredCode; 

@end 


@synthesize enteredCode; 

-(void) addInput:(NSString *) input 
{ 
    self.enteredCode = [self.enteredCode stringByAppendingString:input]; 
} 

- (void)dealloc { 
    [enteredCode release]; 
} 

@end 
+0

Libérer une chaîne de caractères est très bien, ce n'est pas différent de libérer n'importe quel autre NSString. – kubi

+0

Bien sûr, mais il ne sera jamais retenu, donc vous ne devriez pas (et ne devriez pas) le libérer. – MrMage

+0

La libération de quelque chose qui n'a pas été allouée peut causer de sérieux problèmes. – VagueExplanation