Précisions sur NSCoding

skimpyskimpy Membre
09:48 modifié dans API AppKit #1
Bonsoir,

Je viens d'implémenter le protocole NSCoding pour mon projet afin de sauvegarder les données. L'enregistrement et le chargement se font bien mais j'aimerais avoir un renseignement :

Lorsque les données sont "désarchivées", est-ce que l'instanciation d'un objet se fait par le constructeur ? Je demande ça car dans une de mes classes j'utilise un constructeur initWithParameters (et pas le simple init - j'ai bien fait attention de faire un initialisateur désigné)
Qu'en est-il du compteur de référence ? Il est placé automatiquement à  1 ?

Merci.

Réponses

  • fouffouf Membre
    09:48 modifié #2
    Lors du "désarchivage", les objets sont initialisés par initWithCoder: et dont l'argument est un NSCoder dont tu vas pouvoir tirer les infos préalablement enregistrer grâce à  la méthode encodeWithCoder: de ta classe (cf doc NSCoder et NSCoding).
    Ensuite, à  toi de faire ensorte que le initWithCoder: appelle ton initWithParameters: .
    Petit exemple où les paramètres sont sous forme d'un dictionnaire.

    <br />- (id)initWithCoder:(NSCoder *)decoder<br />{<br />&nbsp;  NSString *nom;<br />&nbsp;  id premierParam, secondParam;<br />&nbsp;  <br />&nbsp;  // on va chercher les paramètres enregistrés<br />&nbsp;  nom = [decoder decodeObjectForKey:@&quot;Nom&quot;];<br />&nbsp;  premierParam = [decoder decodeObjectForKey:@&quot;Premier Paramètre&quot;];<br />&nbsp;  secondParam = [decoder decodeObjectForKey:@&quot;Second Paramètre&quot;];<br /><br />&nbsp;  // on crée un dictionnaire contenant les objets<br />&nbsp;  NSDictionary *d;<br />&nbsp;  d = [[NSDictionary alloc] initWithObjectAndKeys:nom,@&quot;Name&quot;,premierParam,@&quot;First Parameter&quot;,secondParam,@&quot;Second Parameter&quot;,nil];<br /> <br />&nbsp;  // initialisation &quot;classique&quot;<br />&nbsp; self = [self initWithParameters:d];<br />&nbsp; <br />&nbsp; // on nettoie (nom, premierParam et secondParam n&#39;ont pas besoin d&#39;être releasé)<br />&nbsp; [d release]; <br /><br />&nbsp; // on finit ...<br />&nbsp; return self;<br />}<br />
    


    Voila, c'est "tout" (attention si tu fais un copier-coller de ce code, je ne suis pas sûr qu'il n'y ait pas d'erreure). Evidemment ca demande que tu aies correctement encodé tes paramètres dans encodeWithCoder:, mais tu l'as apparemment bien fait ;)
  • skimpyskimpy Membre
    09:48 modifié #3
    Merci fouf. En fait, je me suis basé sur un exemple de CocoaDevCentral :

    <br />- (id) initWithCoder:(NSCoder *)coder<br />{<br />	if(self = [super init])<br />	{<br />		[self setData:[coder decodeObjectForKey:@&quot;data&quot;]];<br />		[self setChild:[coder decodeObjectForKey:@&quot;child&quot;]];<br />	}<br />	<br />	return self;<br />}<br />
    


    Ma classe hérite de NSObject et a 2 initialisateurs : - (id)init et - (id)initWithRoot:(NSMutableDictionary *)aData. L'initialisateur init fait appel à  initWithRoot avec des paramètres par défaut.
    Ce que je ne comprends pas c'est le self = [super init] : le "super" appelle bien l'initialisateur de la classe parent (soit NSObject) ? Donc comment les objets data et child sont instancés (j'ai mis un NSLog dans mon init et initWithRoot mais lorsque j'utilise ma fonction de "désarchivage", ils ne sont jamais appelés) ?
  • fouffouf Membre
    09:48 modifié #4
    Je pense que tu as compris le truc ;)
    super appelle la superclasse (classe parent) et non pas ta propre classe et donc, pour initialiser les objets data et child, il faut que tu appelles [self init] (tu t'appelle toi-même en quelque sorte). Et ensuite, tu pourras faire setData: et setChild: comme dans ton code.

    Donc pour que ton code marche, il te suffi de changer le [super init] en [self init] :)
  • skimpyskimpy Membre
    09:48 modifié #5
    Merci fouf, je viens de modifier mon self = [super init] par self = [self init] et là , je le vois bien passer dans mon initialisateur.
    Par contre, quand je faisais mon self = [super init], ça fonctionnait correctement (mes données sauvées sur disque étaient bien affichées lors du chargement de l'appli) ... c'est bizarre ou normal ?
  • fouffouf Membre
    09:48 modifié #6
    C'est normal. Je pense que tes accesseurs (et surtout tes setters) sont de la forme
    <br />- (void)setData:(id)d<br />{<br />&nbsp;  [_data release]; // on nettoie l&#39;ancien<br />&nbsp;  [d retain]; // on s&#39;intéresse au nouveau<br />&nbsp;  _data = d; <br />}<br />
    


    Donc au début, lorsque que tu fais un setData: par exemple, _data == nil le fait que tu fasses un setData: permet de faire que _data pointe sur quelque chose, et donc finalement que tes données s'affichent.
    Mais un problème aurait pus avoir lieu si par exemple, tu avais une données qui s'initialise dans le init mais dont tu ne change pas la valeur après (tu ne fais pas de set...: ). Je n'ai pas d'idée qui me viens à  l'esprit, mais je pense que tu auras compris ;)

    Bonne chance pour la suite :fouf):
  • ChachaChacha Membre
    09:48 modifié #7

    <br />- (void)setData:(id)d<br />{<br />   [_data release]; // on nettoie l&#39;ancien<br />   [d retain]; // on s&#39;intéresse au nouveau<br />   _data = d; <br />}<br />
    



    Aaaatttention ! Il faut faire l'inverse, à  savoir :
    <br />- (void)setData:(id)d<br />{<br />   [d retain]; // on s&#39;intéresse au nouveau<br />   [_data release]; // on nettoie l&#39;ancien<br />   _data = d; <br />}<br />
    


    En effet, imaginons que le paramètre de setData soit _data lui-même, en théorie il ne doit rien se passer. La deuxième solution fait donc +1-1 au compteur de référence (très bien), tandis que la première solution (la mauvaise) fait -1+1. Le problème est que si le compteur était à  1, il passe à  0, donc l'objet est détruit avant d'avoir pu faire le +1.

    (Un retain fait +1, un release fait -1)

    +
    Chacha

  • skimpyskimpy Membre
    09:48 modifié #8
    Oui, il existe aussi la méthode :

    <br />- (void)setData:(id)d<br />{<br />&nbsp;  if(d != _data)<br />&nbsp;  {<br />&nbsp; &nbsp; &nbsp; [_data release];<br />&nbsp; &nbsp; &nbsp; _data = [d retain];<br />&nbsp;  }<br />}<br />
    
Connectez-vous ou Inscrivez-vous pour répondre.