2010-11-23 23 views
0

J'ai créé une liste de propriétés qui, lorsqu'elle n'est pas archivée, initialise un tableau d'instances d'une classe appelée 'SelectableObject'. La classe SelectableObject ne contient pas de méthode init, elle est juste un conteneur pour certaines variables d'instance et est bien entendu conforme à NSCoding.Obligation de conserver une nouvelle instance d'une classe et, par conséquent, une fuite de mémoire

Lorsqu'un objet SelectableObject est choisi, j'initialise une nouvelle instance d'une sous-classe de SelectableObject, en fonction de l'objet sélectionné. Appelez ce 'AnimalObject'.

J'ai écrit un initialiseur personnalisé pour AnimalObject, qui prend un objet SelectableObject comme argument, pour obtenir les valeurs de SelectableObject, puis ajoute une partie de sa propre valeur. Ainsi, la méthode ressemble à ceci:

- (AnimalObject *)initWithObject:(SelectableObject *)selectedObject 

Et je l'appelle de l'écran de sélection comme ceci:

AnimalObject *animal = [[AnimalObject alloc] initWithObject:self.selectedObject]; 

où self.selectedObject est l'objet sélectionné à partir de l'écran de sélection.

Maintenant, dans le initialiseur pour la AnimalObject, je mis les valeurs initiales, puis juste avant de le retourner, appelez

[self saveToFile]; 

pour enregistrer le nouvel objet fichier. Donc, après l'avoir initialisé dans l'écran de sélection, je devrais pouvoir le relâcher tout de suite, n'est-ce pas?

Cependant, si je tente de le libérer, i.e.

AnimalObject *animal = [[AnimalObject alloc] initWithObject:self.selectedObject]; 
[animal release]; 

je reçois un accident.

Si je ne le libère pas, je reçois un avertissement que tous les iVars initialisés dans l'initialiseur AnimalObject fuient la mémoire.

J'ai essayé de créer une propriété AnimalObject dans le selectionScreen et attribuer le nouvel objet à libérer avant, mais cela se bloque encore, à savoir

self.newAnimal = animal; 
[animal release]; 

Je me demande si ma description ci-dessus, je Je fais quelque chose de très mal? Comme enregistrer une classe à partir de son initialiseur. Ou en passant une classe parent en tant qu'objet à un initiateur d'une sous-classe ... Je ne suis pas vraiment sûr pourquoi je ne peux pas libérer l'instance de l'objet que j'ai créé.

Merci pour toute aide!

EDIT Ok, voici mon code:

La classe sélectionnable de l'objet:

#define kObjectNumberKey   @"ObjectNumber" 
#define kObjectTypeKey    @"Type" 
#define kObjectNameKey    @"Name" 
#define kObjectThumbKey    @"Thumb" 
#define kObjectMainKey    @"Main" 

#import <Foundation/Foundation.h> 

@interface SelectableObject : NSObject <NSCoding> { 
    int   number; 
    NSString *type; 
    NSString *name; 
    NSString *thumbString; 
    NSString *mainString; 
} 

@property (nonatomic, assign) int number; 
@property (nonatomic, retain) NSString *type; 
@property (nonatomic, retain) NSString *name; 
@property (nonatomic, retain) NSString *thumbString; 
@property (nonatomic, retain) NSString *mainString; 

@end 

#import "SelectableObject.h" 

@implementation SelectableObject 

@synthesize number; 
@synthesize type; 
@synthesize name; 
@synthesize thumbString; 
@synthesize mainString; 


- (void)dealloc { 
    NSLog(@"Selectable Object DEALLOC"); 
    [name release]; 
    [type release]; 
    [thumbString release]; 
    [mainString release]; 
    [super dealloc]; 
} 

#pragma mark - 
#pragma mark NSCoding 

- (void)encodeWithCoder:(NSCoder *)aCoder { 
    [aCoder encodeInt:self.number forKey:kObjectNumberKey]; 
    [aCoder encodeObject:self.type forKey:kObjectTypeKey]; 
    [aCoder encodeObject:self.name forKey:kObjectNameKey]; 
    [aCoder encodeObject:self.thumbString forKey:kObjectThumbKey]; 
    [aCoder encodeObject:self.mainString forKey:kObjectMainKey]; 
} 

- (id)initWithCoder:(NSCoder *)aDecoder { 
    if (self = [super init]) { 
     self.number = [aDecoder decodeIntForKey:kObjectNumberKey]; 
     self.type = [aDecoder decodeObjectForKey:kObjectTypeKey]; 
     self.name = [aDecoder decodeObjectForKey:kObjectNameKey]; 
     self.thumbString = [aDecoder decodeObjectForKey:kObjectThumbKey]; 
     self.mainString = [aDecoder decodeObjectForKey:kObjectMainKey]; 
    } 
    return self; 
} 

@end 

Le code dans un autre VC pour créer un nouvel objet animal avec les détails du selectedObject:

- (void)createNewAnimal { 
    self.selectedObject.name = self.nameField.text; 

    Animal *animal = [[Animal alloc] initWithObject:self.selectedObject]; 
// [animal release]; (causes it to crash) 

    // Initialise the new Root Controller and load with selected object 
} 

Et dans la classe Animal:

- (Animal *)initWithObject:(SelectableObject *)selectedObject { 
    if (self = [super init]) { 
     //Initialise all the values 
     dateCreated = [[NSDate alloc] init]; 
     dateLastUsed = [[NSDate alloc] init]; 
     name = selectedObject.name; 
     thumbString = selectedObject.thumbString; 
     mainString = selectedObject.mainString; 
    } 
    [self saveToFile]; 
    return self; 
} 
+1

Je ne vois rien d'anormal dans votre description. Cela aiderait à voir le code dans 'initWithObject:' et les déclarations pour vos propriétés dans 'AnimalObject'. En outre, le code de votre méthode 'dealloc' peut également fournir des indices. –

+0

Est-ce que 'selectedObject' est retenu? Ce n'est peut-être pas 'animalObject' qui est le problème, mais ce que vous transmettez en tant que paramètre init. –

+0

Je pense que vous avez raison Stephen. selectedObject est conservé, et si je commente sa sortie dans la méthode dealloc, le programme fonctionne bien! Mis à part le fait qu'il n'est pas libéré bien sûr. Donc je ne devrais pas passer un objet parent retenu en tant que paramètre d'un initialiseur de sa sous-classe? Hmmm - comment puis-je transmettre les détails de l'objet sélectionné tout en libérant l'objet selectedObject? – Smikey

Répondre

1

Votre initWithObject est peut-être un peu suspect. Le dealloc de Animal publie-t-il name, thumbString et mainString? Si c'est le cas, ils sont libérés deux fois.

- (Animal *)initWithObject:(SelectableObject *)selectedObject { 
    if (self = [super init]) { 
     //Initialise all the values 
     dateCreated = [[NSDate alloc] init]; 
     dateLastUsed = [[NSDate alloc] init]; 
     name = [selectedObject.name retain]; // Added retain 
     thumbString = [selectedObject.thumbString retain]; // Added retain 
     mainString = [selectedObject.mainString retain]; // Added retain 
    } 
    [self saveToFile]; 
    return self; 
} 
+0

Non, le dealloc d'Animal ne libère que les iVars qu'il crée, c'est-à-dire dateCreated et dateLastUsed. Il appelle [super dealloc] où le nom, thumbString et mainString sont libérés. – Smikey

+0

Si Animal ne conserve/ne libère PAS ces chaînes, dès que votre objet SelectedObject utilisé comme paramètre de 'initWithObject:' est désalloué, les pointeurs dans Animal deviennent indésirables et leur accès est un comportement indéfini. C'est-à-dire, à moins que vous ayez la garantie que le SelectedObject survivra à Animal, vous DEVEZ conserver ou copier ces propriétés. – imaginaryboy

+0

Bien sûr! Je n'y ai jamais pensé comme ça, mais tu as absolument raison. Je pensais que les initialiser à l'iVars de l'objet Animal serait suffisant pour les conserver, mais comme je n'utilise pas de méthodes d'accesseur (étant dans la méthode init), je suppose que ce n'est pas le cas. L'ajout des retenues l'a corrigé. Merci beaucoup! – Smikey

0

Ceci est un coup de feu dans l'obscurité complète, mais avez-vous essayé

AnimalObject *animal = [[[AnimalObject alloc] initWithObject:self.selectedObject] autorelease]; 
+0

J'ai essayé cela, mais malheureusement, il se bloque toujours.Je suis assez sûr que c'est à voir avec la version de la variable d'instance selectedObject ... – Smikey

+0

probablement juste! bonne chance!^_^ –

1

Comment la méthode dealloc() de Animal ressemble-t-elle? Est-ce que vous libérez le nom, thumbString et mainString là-dedans? Si oui, alors il y a votre crash.

En raison du pistolet le plus rapide du problème occidental, la réponse a déjà été fournie par @imaginaryboy. Comme mainString et thumbString sont libérés dans la super classe, il va planter. Ajoutez simplement un retain dans la méthode init et vous devriez tous les définir.

Remarque:NSString Il convient d'utiliser la copie au lieu de conserver.

+0

Non, ne pas les libérer là. Je pense que c'est l'objet selectable que je passe en argument. On dirait qu'il est sorti dans l'intialiser d'une façon ou d'une autre, alors quand il sort dans la superclasse, il plante ...? – Smikey

+0

Je suis assez sûr que c'est le cas. Vous ne devez libérer que lorsque vous avez conservé et dans ce cas vous n'avez pas conservé les chaînes en question, juste les assignées. Essayez le code de @ imaginaryboy et je suis sûr que ça marchera. –

+0

Super - merci! Le plus rapide ou pas, j'apprécie vraiment l'aide :) – Smikey

0

Je ne sais pas si cela est le problème ici, mais jusqu'à présent, je ne l'ai vu sorcière les méthodes de démarrage retourne un type identifiant, comme

- (id)init 

Je n'ai pas essayé de retourner un autre type que id jusqu'à maintenant.

+0

Je pense que c'est important quand vous sous-classez, de sorte que les sous-classes puissent appeler self = [super init], mais comme je ne le suis pas, j'ai simplement utilisé le type de retour. Probablement une meilleure pratique pour coller à id si, merci pour le pourboire! – Smikey