Je viens de commencer à programmer Objective-C et, ayant une expérience en Java, je me demande comment les personnes qui écrivent des programmes Objective-C traitent les méthodes privées.
Je comprends qu’il peut exister plusieurs conventions et habitudes et j’envisage cette question comme un agrégateur des meilleures techniques que les gens utilisent pour traiter les méthodes privées en Objective-C.
Veuillez inclure un argument pour votre approche lors de sa publication. Pourquoi est-il bon? Quels sont ses inconvénients (à votre connaissance) et comment les gérer?
En ce qui concerne mes conclusions jusqu'à présent.
Il est possible d’utiliser catégories [p. Ex. MyClass (Private)] définie dans le fichier MyClass.m pour regrouper les méthodes privées.
Cette approche a 2 problèmes:
Le premier numéro peut être contourné avec catégorie vide [p. Ex. Ma classe ()].
Le second me dérange beaucoup. J'aimerais voir des méthodes privées implémentées (et définies) vers la fin du fichier; Je ne sais pas si c'est possible.
Comme d'autres l'ont déjà dit, il n'existe pas de méthode privée dans Objective-C. Toutefois, à partir de Objective-C 2.0 (Mac OS X Leopard, iPhone OS 2.0 et ultérieur), vous pouvez créer une catégorie avec un nom vide (c'est-à-dire @interface MyClass ()
) appelée Extension de classe. La particularité d'une extension de classe est que les implémentations de méthodes doivent aller dans le même @implementation MyClass
que les méthodes publiques. Donc, je structure mes cours comme ceci:
Dans le fichier .h:
@interface MyClass {
// My Instance Variables
}
- (void)myPublicMethod;
@end
Et dans le fichier .m:
@interface MyClass()
- (void)myPrivateMethod;
@end
@implementation MyClass
- (void)myPublicMethod {
// Implementation goes here
}
- (void)myPrivateMethod {
// Implementation goes here
}
@end
Je pense que le plus grand avantage de cette approche est qu'elle vous permet de regrouper les implémentations de vos méthodes par fonctionnalité et non par distinction (parfois arbitraire) public/privé.
Il n'y a pas vraiment de "méthode privée" dans Objective-C, si le moteur d'exécution peut déterminer quelle implémentation utiliser, il le fera. Mais cela ne veut pas dire qu'il n'y a pas de méthodes qui ne font pas partie de l'interface documentée. Pour ces méthodes, je pense qu'une catégorie convient. Plutôt que de placer le @interface
en haut du fichier .m comme votre point 2, je le mettrais dans son propre fichier .h. Une convention que je suis (et que j’ai vue ailleurs, je pense que c’est une convention Apple puisque Xcode l’a maintenant automatiquement prise en charge) consiste à nommer un tel fichier après sa classe et sa catégorie avec un + les séparant, donc @interface GLObject (PrivateMethods)
peut être trouvé dans GLObject+PrivateMethods.h
. La raison de la fourniture du fichier d’en-tête est que vous pouvez l’importer dans vos classes de tests unitaires :-).
En passant, en ce qui concerne la mise en œuvre/la définition de méthodes à la fin du fichier .m, vous pouvez le faire avec une catégorie en implémentant la catégorie au bas du fichier .m:
@implementation GLObject(PrivateMethods)
- (void)secretFeature;
@end
ou avec une extension de classe (ce que vous appelez une "catégorie vide"), définissez simplement ces méthodes en dernier. Les méthodes Objective-C peuvent être définies et utilisées dans n'importe quel ordre de l'implémentation. Par conséquent, rien ne vous empêche de placer les méthodes "privées" à la fin du fichier.
Même avec des extensions de classe, je vais souvent créer un en-tête distinct (GLObject+Extension.h
) afin de pouvoir utiliser ces méthodes si nécessaire, en imitant la visibilité "ami" ou "protégée".
Depuis que cette réponse a été écrite à l'origine, le compilateur Clang a commencé à exécuter deux passes pour les méthodes Objective-C. Cela signifie que vous pouvez éviter de déclarer vos méthodes "privées" complètement, et si elles sont situées au-dessus ou au-dessous du site appelant, elles seront trouvées par le compilateur.
Bien que je ne sois pas un expert d'Objective-C, j'ai personnellement défini la méthode dans la mise en œuvre de ma classe. Certes, il doit être défini avant (ci-dessus) toute méthode l’appelant, mais cela demande certainement le moins de travail possible.
Définir vos méthodes privées dans le bloc @implementation
est idéal dans la plupart des cas. Clang les verra dans le @implementation
, quel que soit l'ordre de déclaration. Il n'est pas nécessaire de les déclarer dans une continuation de classe (ou extension de classe) ou une catégorie nommée.
Dans certains cas, vous devrez déclarer la méthode dans la continuation de la classe (par exemple, si vous utilisez le sélecteur entre la continuation de la classe et le @implementation
).
Les fonctions static
conviennent très bien aux méthodes privées particulièrement sensibles ou critiques.
Une convention pour nommer les préfixes peut vous aider à éviter de surcharger accidentellement les méthodes privées (je trouve le nom de la classe comme préfixe safe).
Les catégories nommées (par exemple, @interface MONObject (PrivateStuff)
) ne sont pas une bonne idée en raison des risques de collision de noms lors du chargement. Elles ne sont vraiment utiles que pour les méthodes d'amis ou protégées (qui sont très rarement un bon choix). Pour vous assurer d'être prévenu de l'implémentation incomplète des catégories, vous devez en réalité l'implémenter:
@implementation MONObject (PrivateStuff)
...HERE...
@end
Voici un petit aide-mémoire annoté:
MONObject.h
@interface MONObject : NSObject
// public declaration required for clients' visibility/use.
@property (nonatomic, assign, readwrite) bool publicBool;
// public declaration required for clients' visibility/use.
- (void)publicMethod;
@end
MONObject.m
@interface MONObject ()
@property (nonatomic, assign, readwrite) bool privateBool;
// you can use a convention where the class name prefix is reserved
// for private methods this can reduce accidental overriding:
- (void)MONObject_privateMethod;
@end
// The potentially good thing about functions is that they are truly
// inaccessible; They may not be overridden, accidentally used,
// looked up via the objc runtime, and will often be eliminated from
// backtraces. Unlike methods, they can also be inlined. If unused
// (e.g. diagnostic omitted in release) or every use is inlined,
// they may be removed from the binary:
static void PrivateMethod(MONObject * pObject) {
pObject.privateBool = true;
}
@implementation MONObject
{
bool anIvar;
}
static void AnotherPrivateMethod(MONObject * pObject) {
if (0 == pObject) {
assert(0 && "invalid parameter");
return;
}
// if declared in the @implementation scope, you *could* access the
// private ivars directly (although you should rarely do this):
pObject->anIvar = true;
}
- (void)publicMethod
{
// declared below -- but clang can see its declaration in this
// translation:
[self privateMethod];
}
// no declaration required.
- (void)privateMethod
{
}
- (void)MONObject_privateMethod
{
}
@end
Une autre approche qui peut ne pas être évidente: un type C++ peut être à la fois très rapide et fournir un degré de contrôle beaucoup plus élevé, tout en minimisant le nombre de méthodes objc exportées et chargées.
Vous pouvez essayer de définir une fonction statique en dessous ou au-dessus de votre implémentation qui prend un pointeur sur votre instance. Il pourra accéder à toutes vos variables d'instance.
//.h file
@interface MyClass : Object
{
int test;
}
- (void) someMethod: anArg;
@end
//.m file
@implementation MyClass
static void somePrivateMethod (MyClass *myClass, id anArg)
{
fprintf (stderr, "MyClass (%d) was passed %p", myClass->test, anArg);
}
- (void) someMethod: (id) anArg
{
somePrivateMethod (self, anArg);
}
@end
Vous pourriez utiliser des blocs?
@implementation MyClass
id (^createTheObject)() = ^(){ return [[NSObject alloc] init];};
NSInteger (^addEm)(NSInteger, NSInteger) =
^(NSInteger a, NSInteger b)
{
return a + b;
};
//public methods, etc.
- (NSObject) thePublicOne
{
return createTheObject();
}
@end
Je sais que c'est une vieille question, mais c'est l'une des premières que j'ai trouvées lorsque je cherchais une réponse à cette question. Je n'ai pas vu cette solution discutée ailleurs, alors laissez-moi savoir s'il y a quelque chose d'idiot à faire cela.
chaque objet de l’objectif C est conforme au protocole NSObject, qui s’applique à la méthode performSelector:. J'étais également à la recherche d'un moyen de créer des méthodes "d'assistance ou privées" dont je n'avais pas besoin d'être exposées au niveau public. Si vous souhaitez créer une méthode privée sans surcharge et ne pas avoir à la définir dans votre fichier d'en-tête, essayez-le ...
définissez votre méthode avec une signature similaire au code ci-dessous ...
-(void)myHelperMethod: (id) sender{
// code here...
}
puis, lorsque vous avez besoin de référencer la méthode, appelez-la simplement comme sélecteur ...
[self performSelector:@selector(myHelperMethod:)];
cette ligne de code appellera la méthode que vous avez créée et ne sera pas avertie de ne pas l'avoir définie dans le fichier d'en-tête.
Si vous voulez éviter le bloc @interface
au sommet, vous pouvez toujours mettre les déclarations privées dans un autre fichier MyClassPrivate.h
pas idéal, mais il ne faut pas encombrer l'implémentation.
MyClass.h
interface MyClass : NSObject {
@private
BOOL publicIvar_;
BOOL privateIvar_;
}
@property (nonatomic, assign) BOOL publicIvar;
//any other public methods. etc
@end
MyClassPrivate.h
@interface MyClass ()
@property (nonatomic, assign) BOOL privateIvar;
//any other private methods etc.
@end
MyClass.m
#import "MyClass.h"
#import "MyClassPrivate.h"
@implementation MyClass
@synthesize privateIvar = privateIvar_;
@synthesize publicIvar = publicIvar_;
@end
Une dernière chose que je n'ai pas vue mentionnée ici - Xcode prend en charge les fichiers .h avec le nom "_private". Disons que vous avez une classe MyClass - vous avez MyClass.m et MyClass.h et maintenant vous pouvez également avoir MyClass_private.h. Xcode le reconnaîtra et l'inclura dans la liste des "contreparties" de l'éditeur adjoint.
//MyClass.m
#import "MyClass.h"
#import "MyClass_private.h"
Il n'y a aucun moyen de contourner le problème n ° 2. C’est ainsi que fonctionnent le compilateur C (et donc le compilateur Objective-C). Si vous utilisez l'éditeur XCode, la fonction popup devrait faciliter la navigation dans les blocs @interface
et @implementation
du fichier.
Il y a un avantage de l'absence de méthodes privées. Vous pouvez déplacer la logique que vous souhaitez masquer vers la classe séparée et l'utiliser en tant que délégué. Dans ce cas, vous pouvez marquer un objet délégué comme privé et il ne sera pas visible de l'extérieur. Le fait de déplacer la logique vers une classe séparée (peut-être plusieurs) améliore la conception de votre projet. Parce que vos classes deviennent plus simples et vos méthodes sont regroupées dans des classes avec des noms propres.
Comme d'autres personnes l'ont dit, définir des méthodes privées dans le bloc @implementation
est acceptable dans la plupart des cas.
À propos de organisation du code - J'aime les garder ensemble sous pragma mark private
pour faciliter la navigation dans Xcode
@implementation MyClass
// .. public methods
# pragma mark private
// ...
@end