111 votes

Déclaration/définition des endroits variables ObjectiveC ?

Depuis de commencer à travailler sur les applications iOS et objective-C, j'ai été vraiment surpris par les différents endroits où l'on pouvait être à la déclaration et la définition des variables. Nous avons d'une part la traditionnelle C de l'approche, de l'autre nous avons la nouvelle ObjectiveC directives ajouter OO sur le dessus de cela. Pourriez-vous les gens m'aide à comprendre les meilleures pratiques et les situations où je voudrais utiliser ces emplacements pour mes variables et peut-être en raison de ma compréhension actuelle?

Voici un exemple de classe (.h et .m):

#import <Foundation/Foundation.h>

// 1) What do I declare here?

@interface SampleClass : NSObject
{
    // 2) ivar declarations
    // Pretty much never used?
}

// 3) class-specific method / property declarations

@end

et

#import "SampleClass.h"

// 4) what goes here?

@interface SampleClass()

// 5) private interface, can define private methods and properties here

@end

@implementation SampleClass
{
    // 6) define ivars
}

// 7) define methods and synthesize properties from both public and private
//    interfaces

@end
  • Ma compréhension de la 1 et la 4, c'est que ceux sont des C-style de fichier de la base de déclarations et les définitions qui ont absolument aucune idée de la notion de classe, et doivent donc être utilisé exactement de la façon dont ils seront utilisés en C. je les ai vus utilisé pour la mise en œuvre de variable statique à base de singletons avant. Existe-il d'autres commode utilise je suis absent?
  • Mon prendre de travailler avec iOS, c'est que ivars ont été alost complètement éliminé à l'extérieur de la @synthétiser la directive et donc peut être la plupart du temps ignoré. Est-ce le cas?
  • Sujet 5: pourquoi aurais-je besoin de déclarer les méthodes dans les interfaces privées? Mes méthodes de la classe semblent compiler correctement sans une déclaration dans l'interface. Est-ce principalement pour des raisons de lisibilité?

Merci beaucoup, gens!

151voto

DrummerB Points 22871

Je comprends votre confusion. Surtout depuis les récentes mises à jour de Xcode et le nouveau compilateur LLVM changé la façon dont ivars et les propriétés peuvent être déclarés.

Avant de "moderne" Objective-C (dans les "anciens" Obj-C 2.0) vous n'avez pas beaucoup de choix. Les variables d'Instance utilisé pour être déclarée dans l'en-tête entre les accolades { }:

// MyClass.h
@interface MyClass : NSObject {
    int myVar;
}
@end

Vous avez été en mesure d'accéder à ces variables, la mise en œuvre, mais pas à d'autres classes. Pour ce faire, vous avez dû déclarer des méthodes accesseurs, qui ressemble à quelque chose comme ceci:

// MyClass.h
@interface MyClass : NSObject {
    int myVar;
}

- (int)myVar;
- (void)setMyVar:(int)newVar;

@end


// MyClass.m
@implementation MyClass

- (int)myVar {
   return myVar;
}

- (void)setMyVar:(int)newVar {
   if (newVar != myVar) {
      myVar = newVar;
   }
}

@end

De cette façon, vous étiez en mesure d'obtenir et de définir cette variable d'instance à partir d'autres classes, en utilisant l'habitude crochet syntaxe pour envoyer des messages (l'appel de méthodes):

// OtherClass.m
int v = [myClass myVar];  // assuming myClass is an object of type MyClass.
[myClass setMyVar:v+1];

Parce que manuellement de la déclaration et de la mise en œuvre de chaque méthode d'accesseur était assez ennuyeux, @property et @synthesize ont été introduits pour générer automatiquement les méthodes d'accès:

// MyClass.h
@interface MyClass : NSObject {
    int myVar;
}
@property (nonatomic) int myVar;
@end

// MyClass.m
@implementation MyClass
@synthesize myVar;
@end

Le résultat est beaucoup plus claire et la plus courte de code. Les méthodes d'accès sera mis en place pour vous et vous pouvez toujours utiliser le support de la syntaxe comme avant. Mais en plus, vous pouvez également utiliser la syntaxe à point pour accéder à des propriétés:

// OtherClass.m
int v = myClass.myVar;   // assuming myClass is an object of type MyClass.
myClass.myVar = v+1;

Depuis Xcode 4.4 vous n'avez pas à déclarer une variable d'instance, vous-même et vous pouvez sauter @synthesize trop. Si vous ne déclarez pas une ivar, le compilateur va l'ajouter pour vous et il permettra également de générer les méthodes d'accès sans avoir à utiliser @synthesize.

Le nom par défaut pour le générés automatiquement ivar est le nom ou votre propriété, en commençant par un trait de soulignement. Vous pouvez modifier l'généré ivar nom à l'aide d' @synthesize myVar = iVarName;

// MyClass.h
@interface MyClass : NSObject 
@property (nonatomic) int myVar;
@end

// MyClass.m
@implementation MyClass
@end

Cela fonctionne exactement comme le code ci-dessus. Pour des raisons de compatibilité, vous pouvez toujours déclarer ivars dans l'en-tête. Mais parce que la seule raison pour laquelle vous voulez le faire (et de ne pas déclarer une propriété) est de créer une variable privée, vous pouvez maintenant le faire dans la mise en œuvre de fichier ainsi et c'est la meilleure façon.

Un @interface bloc dans la mise en œuvre de fichier est en fait une Extension et peut être utilisé pour déclarer avant les méthodes (pas besoin de plus) et pour (re)déclarer des propriétés. Vous pourriez par exemple déclarer readonly propriété dans votre en-tête.

@property (nonatomic, readonly) myReadOnlyVar;

et redeclare dans votre fichier de mise en oeuvre en tant que readwrite à être en mesure de définir à l'aide de la syntaxe de la propriété, et pas seulement via un accès direct à la ivar.

Comme pour la déclaration des variables complètement en dehors de tout @interface ou @implementation bloc, oui ce sont de simples variables C et fonctionnent exactement de la même.

42voto

Rob Napier Points 92148

Tout d'abord, lire @DrummerB de réponse. Une bonne vue d'ensemble des tenants et ce que vous devriez faire. Avec cela à l'esprit, à vos questions spécifiques:

#import <Foundation/Foundation.h>

// 1) What do I declare here?

Aucune des définitions de variables rendez-vous ici (techniquement, il est légal de le faire si vous savez exactement ce que vous faites, mais ne jamais le faire). Vous pouvez définir plusieurs autres sortes de choses:

  • typdefs
  • les enums
  • externs

Externes ressemblent à des déclarations de variables, mais elles sont tout simplement une promesse à fait déclarer quelque part d'autre. En ObjC, ils devraient seulement être utilisé pour déclarer des constantes, et, en général, seulement des constantes de chaîne. Par exemple:

extern NSString * const MYSomethingHappenedNotification;

Vous seriez alors dans votre .m le fichier de déclarer la constante réelle:

NSString * const MYSomethingHappenedNotification = @"MYSomethingHappenedNotification";

@interface SampleClass : NSObject
{
    // 2) ivar declarations
    // Pretty much never used?
}

Comme l'a noté DrummerB, c'est l'héritage. Ne pas mettre n'importe quoi ici.


// 3) class-specific method / property declarations

@end

Yep.


#import "SampleClass.h"

// 4) what goes here?

Externe des constantes, comme décrit ci-dessus. Aussi fichier de variables statiques pouvez aller ici. Ce sont les équivalents des variables de classe dans d'autres langues.


@interface SampleClass()

// 5) private interface, can define private methods and properties here

@end

Yep


@implementation SampleClass
{
    // 6) define ivars
}

Mais très rarement. Presque toujours, vous devriez permettre à clang (Xcode) pour créer les variables pour vous. Les exceptions sont généralement autour de la non-ObjC ivars (comme le Fondement de Base d'objets, et surtout des objets en C++ si c'est un ObjC++ classe), ou ivars qui ont bizarre de stockage de la sémantique (comme ivars qui ne correspondent pas à une propriété pour une raison quelconque).


// 7) define methods and synthesize properties from both public and private
//    interfaces

En règle générale, vous ne devriez pas @synthétiser plus. Clang (Xcode) le fera pour vous, et vous devez le laisser.

Au cours des dernières années, les choses sont devenues beaucoup plus simple. L'effet secondaire est qu'il y a maintenant trois époques différentes (la fragilité de l'ABI, Non fragiles ABI, Non fragiles ABI + auto-syntheisze). Alors, quand vous voyez le code plus ancien, il peut être un peu déroutant. Ainsi, la confusion découlant de la simplicité :D

6voto

Metabble Points 2837

Je suis aussi assez nouveau, donc j'espère que je ne vis rien.

1 & 4: C-style de variables globales: ils ont du fichier de grande envergure. La différence entre les deux est que, puisque ce sont des fichier de large, le premier sera disponible à quiconque de l'importation de la tête, alors que le second ne l'est pas.

2: les variables d'instance. La plupart des variables d'instance sont synthétisés et récupérées/set via les accesseurs en utilisant les propriétés, car elle rend la gestion de la mémoire simple et sympathique, ainsi que vous donne facile-à-comprendre la notation point.

6: mise en Œuvre ivars sont un peu nouveau. C'est un bon endroit pour mettre privé ivars, puisque vous voulez exposer uniquement ce qui est nécessaire dans l'en-tête public, mais les sous-classes n'hérite pas d'eux autant que je sache.

3 & 7: méthode Publique et des déclarations de propriété, puis mises en œuvre.

5: interface Privée. J'ai toujours utiliser les interfaces privées dès que je peux pour garder les choses propres et de créer une sorte de boîte noire de l'effet. Si ils n'ont pas besoin de la connaître, de le mettre là. J'ai aussi le faire pour des raisons de lisibilité, je ne sais pas si il y a d'autres raisons.

5voto

Jano Points 37593

C'est un exemple pour tous les types de variables déclarées en Objective-C. le nom de La variable indique son accès.

Fichier: Animal.h

@interface Animal : NSObject
{
    NSObject *iProtected;
@package
    NSObject *iPackage;
@private
    NSObject *iPrivate;
@protected
    NSObject *iProtected2; // default access. Only visible to subclasses.
@public
    NSObject *iPublic;
}

@property (nonatomic,strong) NSObject *iPublic2;

@end

Fichier: Animal.m

#import "Animal.h"

// Same behaviour for categories (x) than for class extensions ().
@interface Animal(){
@public
    NSString *iNotVisible;
}
@property (nonatomic,strong) NSObject *iNotVisible2;
@end

@implementation Animal {
@public
    NSString *iNotVisible3;
}

-(id) init {
    self = [super init];
    if (self){
        iProtected  = @"iProtected";
        iPackage    = @"iPackage";
        iPrivate    = @"iPrivate";
        iProtected2 = @"iProtected2";
        iPublic     = @"iPublic";
        _iPublic2    = @"iPublic2";

        iNotVisible   = @"iNotVisible";
        _iNotVisible2 = @"iNotVisible2";
        iNotVisible3  = @"iNotVisible3";
    }
    return self;
}

@end

Notez que le iNotVisible les variables ne sont pas visibles à partir de toute autre classe. C'est une visibilité question, déclarant avec @property ou @public ne change pas.

À l'intérieur d'un constructeur c'est une bonne pratique pour accéder à des variables déclarées avec @property à l'aide de trait de soulignement au lieu self pour éviter les effets secondaires.

Nous allons essayer d'accéder aux variables.

Fichier: Vache.h

#import "Animal.h"
@interface Cow : Animal
@end

Fichier: Vache.m

#import "Cow.h"
#include <objc/runtime.h>

@implementation Cow

-(id)init {
    self=[super init];
    if (self){
        iProtected    = @"iProtected";
        iPackage      = @"iPackage";
        //iPrivate    = @"iPrivate"; // compiler error: variable is private
        iProtected2   = @"iProtected2";
        iPublic       = @"iPublic";
        self.iPublic2 = @"iPublic2"; // using self because the backing ivar is private

        //iNotVisible   = @"iNotVisible";  // compiler error: undeclared identifier
        //_iNotVisible2 = @"iNotVisible2"; // compiler error: undeclared identifier
        //iNotVisible3  = @"iNotVisible3"; // compiler error: undeclared identifier
    }
    return self;
}
@end

On peut encore accéder à la pas visible de variables à l'aide du moteur d'exécution.

Fichier: Vache.m (partie 2)

@implementation Cow(blindAcess)

- (void) setIvar:(NSString*)name value:(id)value {
    Ivar ivar = class_getInstanceVariable([self class], [name UTF8String]);
    object_setIvar(self, ivar, value);
}

- (id) getIvar:(NSString*)name {
    Ivar ivar = class_getInstanceVariable([self class], [name UTF8String]);
    id thing = object_getIvar(self, ivar);
    return thing;
}

-(void) blindAccess {
    [self setIvar:@"iNotVisible"  value:@"iMadeVisible"];
    [self setIvar:@"_iNotVisible2" value:@"iMadeVisible2"];
    [self setIvar:@"iNotVisible3" value:@"iMadeVisible3"];
    NSLog(@"\n%@ \n%@ \n%@",
          [self getIvar:@"iNotVisible"],
          [self getIvar:@"_iNotVisible2"],
          [self getIvar:@"iNotVisible3"]);
}

@end

Nous allons essayer d'accéder à la pas visible variables.

Fichier: main.m

#import "Cow.h"
#import <Foundation/Foundation.h>
int main(int argc, char *argv[]) {
    @autoreleasepool {
        Cow *cow = [Cow new];
        [cow performSelector:@selector(blindAccess)];
    }
}

Cette affiche

iMadeVisible 
iMadeVisible2 
iMadeVisible3

Notez que j'ai pu accéder à la sauvegarde de ivar _iNotVisible2 qui est privé à la sous-classe. En Objective-C toutes les variables peuvent être lues ou d'un ensemble, même de ceux qui sont marqués @private, pas d'exceptions.

Je ne comprend pas associés à des objets ou des variables C qu'ils sont différents oiseaux. Comme pour les variables C, toute variable définie à l'extérieur de @interface X{} ou @implementation X{} est une variable C avec la portée du fichier et stockage statique.

Je n'ai pas de discuter de la gestion de la mémoire attributs, ou readonly/readwrite, de lecture/définition des attributs.

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