Question

J'ai plusieurs années d'expérience dans Obj-c et Cocoa, mais je reviens tout à l'heure à cela et aux avancées d'Obj-C 2.0, etc.

J'essaie de comprendre le moteur d'exécution moderne et de déclarer les propriétés, etc. Une chose qui me laisse un peu perplexe est la possibilité, dans le moteur d'exécution moderne, de créer implicitement les iVars. Et bien sûr, cela implique que dans votre code, vous devriez toujours utiliser self.property pour accéder à la valeur.

Cependant, dans les méthodes init * et dealloc (en supposant que vous n'utilisez pas GC), nous devrions utiliser directement iVar (dans le runtime actuel).

Les questions sont donc les suivantes:

  1. Devrions-nous utiliser des accesseurs de propriétés dans init * et dealloc avec Modern Runtime?

  2. Si oui , pourquoi est-ce différent? Est-ce juste parce que le compilateur ne peut pas voir l'iVar?

  3. Si je dois remplacer un accesseur, puis-je quand même accéder à cet iVar qui sera défini à l'exécution ou dois-je définir un iVar réel que le moteur d'exécution utilisera ensuite?

  4. Encore une fois, si je peux accéder à l'iVar synthétisé , pourquoi ne puis-je pas continuer à le faire pour les méthodes init * et dealloc?

J'ai lu les documents plusieurs fois, mais ils semblaient un peu vagues sur tout cela et je veux être sûr de bien les comprendre afin de décider de la manière dont je veux continuer à coder.

J'espère que mes questions sont claires.

Résumé rapide des tests :

  1. Si vous ne déclarez pas l'ivar dans l'héritage, le compilateur est complètement malheureux

  2. Si vous utilisez #ifndef __OBJC2 __ autour d'ivar dans le compilateur hérité, vous pouvez utiliser ivar directement et en tant que propriété

  3. Dans le runtime moderne, vous pouvez laisser ivar non défini et accéder en tant que propriété

  4. Dans le moteur d’exécution moderne, toute tentative d’accès direct à ivar sans déclaration génère une erreur lors de la compilation

  5. @private La déclaration de ivar permet bien sûr un accès direct à ivar, à la fois classique et moderne

Cela ne donne-t-il pas vraiment une façon propre d’aller de l’avant maintenant?

Était-ce utile?

La solution

Dans le compilateur actuel (OS X 10.5 / GCC 4.0.1), vous ne pouvez pas accéder directement aux ivars synthétisés à l'exécution. Greg Parker, l’un des ingénieurs d’exécution d’OS X, a déclaré de cette manière sur la liste de cacao-dev (12 mars 2009):

  

Vous ne pouvez pas dans le compilateur actuel. UNE   futur compilateur devrait résoudre ce problème. Utilisation   ivars @private explicites dans le   en attendant. Un @private ivar ne devrait pas   être considéré comme faisant partie du contrat -   c'est ce que @private signifie, forcé   par les avertissements du compilateur et l'éditeur de liens   erreurs.

     

Et pourquoi n'y a-t-il pas moyen de   déclarer explicitement les variables d'instance   dans le fichier .m pour le nouveau runtime?

     

Trois raisons: (1) il y en a   détails de conception non triviaux à travailler   out, (2) compiler-engineer-hours sont   limité, et (3) les @private ivars sont   généralement assez bon.

Donc, pour le moment, vous devez utiliser la notation par points pour accéder aux propriétés, même dans init et dealloc . Cela va à l'encontre de la meilleure pratique consistant à utiliser les ivars directement dans ces cas, mais il n'y a pas d'autre solution. Je trouve que la facilité d'utilisation des ivars synthétisés à l'exécution (et les avantages en termes de performances) l'emportent sur cette performance dans la plupart des cas. Si vous avez besoin d'accéder directement à ivar, vous pouvez utiliser un @private ivar comme le suggère Greg Parker (rien ne vous empêche de mélanger des ivars explicitement déclarés et synthétisés à l'exécution).

Mettre à jour Sous OS X 10.6, le moteur d'exécution 64 bits permet un accès direct aux ivars synthétisés via self- & iv.ivar .

Autres conseils

Etant donné que les variables d'instance elles-mêmes ne peuvent être synthétisées que dans le moteur d'exécution moderne (et doivent être déclarées dans l'interface @ sous 32 bits ou avant Léopard), il est plus sûr / plus portable de déclarer également l'ivar

  
      
  • Devrions-nous utiliser des accesseurs de propriétés dans init * et dealloc avec Modern Runtime?
  •   

Ma règle générale est "éventuellement". pour -init * et "généralement pas" pour -dealloc .

Lors de l'initialisation d'un objet, vous voulez vous assurer de copier / conserver correctement les valeurs d'ivars. Sauf si le paramètre de définition de la propriété a un effet secondaire qui le rend inapproprié pour l'initialisation, réutilisez définitivement l'abstraction fournie par la propriété.

Lorsque vous désallouez un objet, vous souhaitez libérer tous les objets ivar, mais pas en stocker de nouveaux. Une méthode simple consiste à définir la propriété sur nil ( myObject.myIvar = nil ), qui appelle essentiellement [myObject setMyIvar: nil] . Puisque les messages à néant sont ignorés, il n'y a aucun danger à cela. Cependant, c'est exagéré lorsque [version de myIvar]; est généralement tout ce dont vous avez besoin. En général, n'utilisez pas la propriété (ni directement le séparateur) dans des situations où la désallocation devrait se comporter différemment de la définition de la variable.

Je peux comprendre l'argument de eJames contre l'utilisation d'accesseurs de propriété dans init / dealloc, mais le revers de la médaille est que si vous modifiez le comportement de la propriété (par exemple, changez de conserver en copier, ou simplement d'assigner sans conserver) et de ne pas Ne l’utilisez pas dans init, ou inversement, le comportement peut aussi ne pas être synchronisé. Si l'initialisation et la modification d'un ivar doivent agir de la même manière, utilisez l'accesseur de propriété pour les deux.

  
      
  • Si oui, pourquoi est-ce différent? Est-ce juste parce que le compilateur ne peut pas voir l'ivar?
  •   

Le moteur d’exécution moderne s’intéresse plus intelligemment à la taille et à la disposition des classes. C’est pourquoi vous pouvez modifier la disposition des ivars sans recompiler les sous-classes. Il est également possible de déduire le nom et le type de l'ivar souhaité à partir du nom et du type de la propriété correspondante. Le Guide de programmation Runtime d'Objective-C 2.0 contient plus d'informations, mais encore une fois, Je ne sais pas à quel point les détails y sont expliqués.

  
      
  • Si je dois remplacer un accesseur, puis-je quand même accéder à cet iVar qui sera défini à l'exécution ou dois-je définir un iVar réel que le moteur d'exécution utilisera ensuite?
  •   

Je n’ai pas testé cela, mais je pense que vous êtes autorisé à accéder au code nommé ivar, car il doit en réalité être créé. Je ne sais pas si le compilateur va se plaindre, mais je suppose que, puisqu'il vous permet de synthétiser l'ivar sans vous plaindre, il est également assez intelligent pour connaître l'ivar synthétisé et vous permettre de le nommer par son nom.

  
      
  • Encore une fois, si je peux accéder à l'iVar synthétisé, pourquoi ne puis-je pas continuer à le faire pour les méthodes init * et dealloc?
  •   

Vous devriez pouvoir accéder à la propriété et / ou à ivar à tout moment une fois l'instance allouée.

Il y a une autre question SO avec des informations similaires, mais ce n’est pas un doublon.

La ligne du bas, tirée de la documentation Objective-C 2.0. , et cité dans La réponse de Mark Bessey est la suivante:

  

Il existe des différences de comportement qui dépendent de l'exécution (voir aussi "Différences d'exécution" & # 8221;):

     

Pour les environnements d'exécution hérités, les variables d'instance doivent déjà être déclarées dans le bloc @interface. Si une variable d'instance du même nom et du même type compatible que la propriété existe, elle est utilisée & # 8212; sinon, vous obtenez une erreur de compilation.

     

Pour les environnements d’exécution modernes, les variables d’instance sont synthétisées selon les besoins. Si une variable d'instance du même nom existe déjà, elle est utilisée.

Je comprends ce qui suit:

Vous ne devez pas utiliser les accesseurs de propriété dans les méthodes init * et dealloc , pour les mêmes raisons que vous ne devriez pas les utiliser dans le runtime hérité: Cela vous laisse ouvert erreurs potentielles si vous substituez ultérieurement les méthodes de la propriété et finissez par faire quelque chose qui ne devrait pas être fait dans init * ou dealloc .

Vous devriez être capable de synthétiser ivar et de remplacer les méthodes de propriété comme suit:

@interface SomeClass
{
}
@property (assign) int someProperty;
@end

@implementation SomeClass
@synthesize someProperty; // this will synthesize the ivar
- (int)someProperty { NSLog(@"getter"); return someProperty; }
- (void)setSomeProperty:(int)newValue
{
    NSLog(@"setter");
    someProperty = newValue;
}
@end

Ce qui me porte à penser que vous seriez en mesure d'accéder au ivar synthétisé dans vos méthodes init * et dealloc . La seule chose à laquelle je puisse penser, c’est que la ligne @synthèse peut devoir venir avant avant les définitions de votre init * et de dealloc méthodes dans le fichier source.

Au bout du compte, étant donné que les ivars déclarés dans l'interface fonctionnent toujours, cela reste votre pari le plus sûr.

Je rencontre le même problème. Je ne parviens pas à accéder aux variables d'instance synthétisées de la manière suivante:

en-tête public

@interface MyObject:NSObject {
}
@property (retain) id instanceVar;
@property (retain) id customizedVar;
@end

en-tête privé / implémentation

@interface MyObject()
@property (retain) id storedCustomizedVar;
@end

@implementation MyObject
@synthesize instanceVar, storedCustomizedVar;
@dynamic customizedVar;

- customizedVar {
  if(!self.storedCustomizedVar) {
    id newCustomizedVar;
    //... do something
    self.storedCustomizedVar= newCustomizedVar;
  }
  return self.storedCustomizedVar;
}

- (void) setCustomizedVar:aVar {
  self.storedCustomizedVar=aVar;
}

@end

Ce n’est pas si élégant, mais au moins, cela garde mon fichier d’en-tête public propre.

Si vous utilisez KVO, vous devez définir customVar en tant que clé dépendante destockée sur mesure.

Je suis relativement nouveau dans Obj-C (mais pas dans la programmation) et j'ai également été dérouté par ce sujet.

Ce qui me préoccupe, c’est qu’il semble relativement facile d’utiliser par inadvertance l’iVar au lieu de la propriété. Par exemple, écrire:

myProp = someObject;

au lieu de

self.myProp = someObject;

Certes, il s'agit de "l'utilisateur". erreur, mais il semble que cela reste assez facile à faire accidentellement dans certains codes, et pour une propriété retenue ou atomique, cela pourrait vraisemblablement poser des problèmes.

Idéalement, je préférerais que le moteur d'exécution applique un motif au nom de la propriété lors de la génération de any iVar. Par exemple. toujours les préfixer avec "_".

En pratique, pour le moment, je le fais manuellement - en déclarant explicitement mes ivars et en leur attribuant délibérément des noms différents de ceux des propriétés. J'utilise un préfixe 'm' à l'ancienne, donc si ma propriété est "myProp", ma propriété iVar sera "mMyProp". Ensuite, j'utilise @synthesize myProp = mMyProp pour associer les deux.

C’est un peu maladroit, je l’admets, et un peu de dactylographie supplémentaire, mais il me semble utile de pouvoir distinguer un peu plus clairement l’ambiguïté du code. Bien sûr, je peux toujours me tromper et taper mMyProp = someObject, mais j'espère que le préfixe 'm' m'avertira de mon erreur.

Je me sentirais beaucoup mieux si je pouvais simplement déclarer la propriété et laisser le compilateur / le moteur d’exécution faire le reste, mais quand j’ai beaucoup de code, mon instinct me dit que je vais faire des erreurs de cette façon si je dois encore le faire. suivez les règles manuelles pour init / dealloc.

Bien sûr, il y a beaucoup d'autres choses que je peux aussi faire de mal ...

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