50 votes

Variables d'instance en Objective-C ?

Je suis sûr que ma confusion est due au fait que je suis coincé dans la "mentalité Java" et que je ne comprends pas en quoi Obj-C diffère dans ce cas.

En Java, je peux déclarer une variable dans une classe, comme ceci, et chaque instance de cette classe aura la sienne :

MyClass {

  String myVar;

  MyClass() {
    // constructor
  }
}

Dans Obj-C, j'ai essayé de faire la même chose en déclarant une variable uniquement dans le fichier .m comme ceci :

#import "MyClass.h"

@implementation MyClass

NSString *testVar;

@end

Je m'attendais à ce que cette variable ait une portée limitée à cette classe. J'ai donc créé une deuxième classe (identique) :

#import "MySecondClass.h"

@implementation MySecondClass

NSString *testVar;

@end

Ce que je constate (et qui me laisse perplexe), c'est que la modification d'une variable dans une classe affecte la valeur observée dans l'autre classe. En fait, si je place un point d'arrêt, puis "Jump to Definition" de la variable, cela m'amène à l'écran d'accueil de la classe.

J'ai créé un très petit projet Xcode qui démontre le problème ici .

1 votes

Objective-C est plus proche de C/C++ que de Java. Java est relativement unique en ce sens qu'il n'a pas de fichiers de déclaration et d'implémentation séparés, mais qu'il met tout dans un seul fichier. En Objective-C, vous déclarez les champs d'instance dans la section @interface de votre fichier .h.

141voto

rmaddy Points 79279

Changez cela :

@implementation MyClass

NSString *testVar;

@end

à :

@implementation MyClass {
    NSString *testVar;
}

// methods go here

@end

et vous obtiendrez ce que vous attendiez.

Comme vous l'avez fait, vous créez en fait une variable globale. Les deux variables globales ont été combinées en une seule par l'éditeur de liens, ce qui explique pourquoi elles ont toutes deux changé lorsque vous en avez défini une. La variable entre accolades sera une variable d'instance propre (et privée).

Edit : Après avoir été downvoted sans raison apparente, je me suis dit que j'allais souligner l'"ancienne" façon de faire les choses, et la nouvelle.

L'ancienne méthode :

SomeClass.h

@interface SomeClass : UIViewController <UITextFieldDelegate> {
    UITextField *_textField;
    BOOL _someBool;
}

@property (nonatomic, assign) BOOL someBool;

// a few method declarations

@end

SomeClass.m

@implementation SomeClass

@synthesize someBool = _someBool;

// the method implementations

@end

Maintenant, la nouvelle méthode améliorée avec le compilateur moderne Objective-C :

SomeClass.h

@interface SomeClass : UIViewController

@property (nonatomic, assign) BOOL someBool;

// a few method declarations

@end

SomeClass.m

@interface SomeClass () <UITextFieldDelegate>
@end

@implementation SomeClass {
    UITextField *_textField;
}

// the method implementations

@end

La nouvelle méthode présente plusieurs avantages. Le premier avantage est qu'aucun des détails spécifiques à l'implémentation de la classe n'apparaît dans le fichier .h. Un client n'a pas besoin de savoir de quels délégués l'implémentation a besoin. Le client n'a pas besoin de savoir quels ivars j'utilise. Maintenant, si l'implémentation a besoin d'un nouvel ivar ou d'un nouveau protocole, le fichier .h ne change pas. Cela signifie que moins de code est recompilé. C'est plus propre et beaucoup plus efficace. Cela facilite également l'édition. Lorsque je suis en train d'éditer le fichier .m et que je réalise que j'ai besoin d'un nouvel ivar, je fais le changement dans le même fichier .m que je suis déjà en train d'éditer. Pas besoin de faire des allers-retours.

Notez également que la mise en œuvre n'a plus besoin d'un ivar ou d'un @synthesize pour le bien.

0 votes

Je pense que c'est parce que vous ne décrivez pas la forme canonique de @interface/@implementation. Ce que vous montrez est techniquement autorisé mais n'est pratiquement jamais utilisé et pourrait être désastreusement déroutant pour un débutant.

1 votes

@HotLicks Ce que je montre est la nouvelle approche privilégiée. Il n'y a plus de raison de mettre des ivars privés dans le fichier .h. Pas avec le compilateur LLVM moderne. Les débutants devraient apprendre la nouvelle méthode, pas l'ancienne.

0 votes

@HotLicks J'ai édité ma réponse pour indiquer les anciennes et les nouvelles façons de coder cela.

0voto

uliwitness Points 2085

Ce que vous voulez probablement (à moins que vous n'utilisiez un système d'exploitation et un compilateur très anciens), c'est utiliser la syntaxe des propriétés. C'est à dire :

@interface MyClass : NSObject

// method declarations here ...

@property (copy) NSString*    myVar;

// ... or here.

@end

Cela fera ce que vous vouliez faire. Elle synthétise implicitement une variable d'instance et une paire getter/setter pour cette variable. Si vous avez voulu créer manuellement la variable d'instance (vous n'en avez généralement pas besoin à moins que votre code ne doive fonctionner sur de très vieilles versions de MacOS), voici ce que le code ci-dessus fait sous le capot pour créer l'ivar :

@interface MyClass : NSObject
{
    NSString*    _myVar;
}

// method declarations here.

@end

Notez les accolades, qui indiquent au compilateur qu'il ne s'agit pas simplement d'une variable globale située entre les méthodes, mais bien d'une variable d'instance appartenant à cet objet.

Si vous créez la propriété uniquement pour un usage interne et que vous ne voulez pas que les clients de votre classe la manipulent, vous pouvez la cacher un peu dans tous les compilateurs ObjC, sauf les plus anciens, en utilisant une propriété de type extension de classe qui "poursuit" la déclaration de classe de l'en-tête, mais qui peut être placée séparément (donc généralement dans votre fichier d'implémentation). Une extension de classe ressemble à une catégorie sans nom :

@interface MyClass ()

@property (copy) NSString*    myVar;

@end

Et vous pouvez y mettre votre déclaration de propriété, ou même des déclarations ivar (toujours entre crochets). Vous pouvez même déclarer la même propriété que readonly dans l'interface de la classe, puis la re-déclarer à l'identique, mais en tant que readwrite dans l'extension, de sorte que les clients ne puissent que la lire, mais que votre code puisse la modifier.

Notez que si vous n'utilisez pas l'ARC (c'est-à-dire si vous avez désactivé le comptage automatique des références par défaut), vous devrez définir toutes vos propriétés comme suit nil dans votre dealloc (à moins qu'ils ne soient réglés sur weak o assign bien sûr).

NB - Tous les éléments ci-dessus sont @interface sections. Votre code réel sera placé dans des sections distinctes @implementation sections. Cela permet d'avoir des fichiers d'en-tête ( .h ) que vous pouvez remettre aux clients de votre classe et qui ne contiennent que les parties que vous voulez qu'ils utilisent, et qui cachent les détails de l'implémentation dans le fichier d'implémentation ( .m ) où vous pouvez les modifier sans avoir à craindre que quelqu'un les ait accidentellement utilisés et que vous cassiez d'autres codes.

PS - Notez que NSStrings et d'autres objets que vous voulez immuables, mais qui existent aussi en version mutable (c.-à-d. NSMutableString ) devrait toujours être copy car cela transformera une NSMutableString en NSString, de sorte que personne à l'extérieur ne pourra modifier la chaîne mutable qui se trouve en dessous de vous. Pour tous les autres types d'objets, vous utilisez généralement strong (ou retain s'il ne s'agit pas d'un ARC). Pour le propriétaire de votre classe (par exemple son délégué), vous utilisez généralement weak (ou assign s'il ne s'agit pas d'un ARC).

-1voto

En Objective-C, vous définissez la variable comme privée en procédant comme suit

MyClass.h

@interface MyClass : NSObject{

      NSString* _myTestVar; // Declaration
}
@end

et y faire référence dans la classe d'implémentation en procédant comme suit MaClasse.m

#import "MyClass.h";

@implementation MyClass

  -(void)initializieTheString
  {
     _myTestVar= @"foo"; //Initialization

  }

@end

-1voto

Gangcil Points 174

En Java

MyClass {

  String myVar;
  MyClass() {
    // constructor
  }
}

En Objective-c

MyClass.h

@interface MyClass : NSObject{

      NSString* str; // Declaration
}
@end

MyClass.m

@implementation MyClass

  -(void)initializieTheString
  {
     //Defination 
  }

@end

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X