Question

//creates memory leak
  self.editMyObject = [[MyObject alloc] init];

//does not create memory leak
  MyObject *temp = [[MyObject alloc] init];
  self.editMyObject = temp;
  [temp release];

La première ligne de code crée une fuite de mémoire, même si vous exécutez [version de self.editMyObject] dans la méthode dealloc de la classe. self.editMyObject est de type MyObject. La deuxième ligne n'entraîne aucune fuite de mémoire. La première ligne est-elle juste incorrecte ou existe-t-il un moyen de libérer de la mémoire?

Était-ce utile?

La solution

Le comportement correct dépend de la déclaration de la propriété editMyObject @. En supposant qu'il soit écrit comme

@property (retain) id editMyObject; //id may be replaced by a more specific type

ou

@property (copy) id editMyObject;

puis l'affectation via self.editMyObject = conserve ou copie l'objet attribué. Étant donné que [[MyObject alloc] init] renvoie un objet conservé, celui-ci, en tant que propriétaire de l'appelant, conserve une instance supplémentaire de l'instance de MyObject. dans le deuxième bloc). Je vous suggère de lire la programmation de la gestion de la mémoire. Guide [2].

Votre deuxième bloc de code est correct, en supposant que la propriété est déclarée comme décrit ci-dessus.

p.s. Vous ne devez pas utiliser [version de self.editMyObject] dans une méthode -dealloc . Vous devez appeler [editMyObject release] (en supposant que l'ivar sauvegardant la propriété @ s'appelle editMyObject ). L'appel de l'accesseur (via self.editMyObject est sans danger pour les accesseurs synthétisés, mais si un accesseur substitué s'appuie sur l'état de l'objet (qui peut ne pas être valide à l'emplacement de l'appel dans -dealloc ou provoque d'autres effets secondaires, vous avez un bogue en appelant l'accesseur.

[2] Les règles de propriété des objets dans Cocoa sont très simples: si vous appelez une méthode dont alloc ou copiez dans sa signature (ou utilisez + [NSObject new] qui est fondamentalement équivalent à [[NSObject alloc] init] ), alors vous "possédez". l'objet qui est renvoyé et vous devez équilibrer votre acquisition de propriété avec une version . Dans tous les autres cas, vous ne possédez pas l'objet renvoyé par une méthode. Si vous souhaitez le conserver, vous devez en prendre possession avec un conserver , puis libérer le propriétaire avec une version .

Autres conseils

Votre propriété est déclarée " conserver " ce qui signifie que l'objet transmis est automatiquement conservé.

Parce que votre objet avait déjà un nombre de références de un parmi alloc / init, il y a alors deux références et je suppose une seule version (dans votre destructeur).

En gros, l'appel à self.editMyObject est en train de le faire;

-(void) setEditMyObject:(MyObject*)obj
{
  if (editMyObject)
  {
    [editMyObject release];
    editMyObject = nil;
  }

  editMyObject = [obj retain];
}

Par convention dans Cocoa et Cocoa-touch, tout objet créé à l'aide de [[SomeClass alloc]] initX] ou [SomeClass newX] est créé avec un nombre de conservations égal à un. . Vous êtes responsable d'appeler [version de someClassInstance] lorsque vous avez terminé avec votre nouvelle instance, généralement dans votre méthode dealloc .

Cela devient difficile lorsque vous affectez votre nouvel objet à une propriété plutôt qu’à une variable d’instance. La plupart des propriétés sont définies en tant que rétention ou copie , ce qui signifie qu'elles incrémentent le nombre de rétention de l'objet lorsqu'il est défini ou en effectuent une copie, en laissant l'original intact.

Dans votre exemple, vous avez probablement ceci dans votre fichier .h :

@property (retain) MyObject *editMyObject;

Donc, dans votre premier exemple:

// (2) property setter increments retain count to 2
self.editMyObject = 

    // (1) new object created with retain count of 1
    [[MyObject alloc] init];

// oops! retain count is now 2

Lorsque vous créez votre nouvelle instance de MyObject à l'aide de alloc / init , son nombre de conservation est égal à un. Lorsque vous affectez la nouvelle instance à self.editMediaObject , vous appelez en réalité la méthode -setEditMyObject: que le compilateur crée pour vous lorsque vous @synthesize editMyObject . Lorsque le compilateur voit self.editMeditObject = x , il le remplace par [self setEditMyObject: x] .

Dans votre deuxième exemple:

MyObject *temp = [[MyObject alloc] init];
// (1) new object created with retain count of 1

self.editMyObject = temp;
// (2) equivalent to [self setEditMyObject: temp];
// increments retain count to 2

[temp release];
// (3) decrements retain count to 1

vous conservez votre nouvel objet assez longtemps pour le libérer, le nombre de retenues est donc équilibré (en supposant que vous le libériez dans votre méthode dealloc ).

Voir aussi Stratégie de cacao pour la gestion de pointeur / mémoire

La première version crée un objet sans version correspondante. Lorsque vous allouez l'objet, cela signifie que vous en êtes le propriétaire. Votre poseur conserve vraisemblablement l'objet (comme il se doit), ce qui signifie que vous possédez maintenant l'objet deux fois. Vous avez besoin de la version pour équilibrer la création d'objet.

Vous devriez lire le guide de gestion de la mémoire de Cocoa si vous envisagez d'utiliser du cacao du tout. Ce n’est pas difficile une fois que vous l’apprenez, mais c’est quelque chose que vous devez apprendre, sinon vous aurez beaucoup de problèmes de ce genre.

Tout le monde a déjà expliqué pourquoi cela provoque une fuite de mémoire, je vais donc expliquer comment éviter la variable "temp" tout en évitant une fuite de mémoire:

self.editMyObject = [[[MyObject alloc] init] autorelease];

Cela laissera votre propriété (conserver) en tant que propriétaire unique du nouvel objet. Exactement le même résultat que votre deuxième exemple, mais sans l'objet temporaire.

Il a été convenu et expliqué que le code ci-dessous n'a pas de fuite (en supposant que @property rétention et @ synthétise pour editMyObject):

//does not create memory leak
MyObject *temp = [[MyObject alloc] init];
self.editMyObject = tempt;
[temp release];

Question: y a-t-il un problème avec le code suivant qui n'utilise pas de pointeur temporaire?

//does not create memory leak ?
self.editMyObject = [[MyObject alloc] init];
[editMyObject release];

Pour moi, ça a l'air bien.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top