web-dev-qa-db-fra.com

Déclarations à terme du protocole Objective-C

ObjectProperties.h

@protocol ObjectProperties <NSObject>

@property (strong, nonatomic) NSString *name;
@property (strong, nonatomic) NSDate *date;
@property (assign, nonatomic) int64_t index;

@end

ClassA.h

#import <Foundation/Foundation.h>

@protocol ObjectProperties;

@interface ClassA : NSObject <ObjectProperties>

- (void)specialSauce;

@end;

ManagedClassA.h

#import <CoreData/CoreData.h>

@protocol ObjectProperties;

@interface ManagedClassA : NSManagedObject <ObjectProperties>

- (void)doSomething;

@end;

À partir de l'exemple de code ci-dessus, j'ai défini un protocole dans un fichier .h à utiliser avec les objets Core Data ainsi qu'avec les objets Vanilla simples. Il semble "bruit" d'avoir des classes conformes #importer le protocole dans leur en-tête; il serait plus propre de déclarer le protocole et d'importer dans le fichier d'implémentation comme je l'ai montré ci-dessus. Cependant, Xcode lance un avertissement en procédant de cette façon:

Cannot find protocol definition for 'ObjectProperties'

Le code se compile et fonctionne principalement. Je dis surtout parce qu'il y a du fun avec Core Data qui essaie de créer dynamiquement les getters/setters pour la propriété scalaire, mais je pense que c'est probablement parce que j'ai rencontré un cas Edge.

Bien sûr, le travail le plus évident consiste à simplement importer l'en-tête de protocole dans les en-têtes de classe.

Si ma compréhension est correcte (et mes connaissances ont été acquises très récemment, et il est donc tout à fait possible que je me trompe), si j'importe le protocole dans mes en-têtes de classe et que j'apporte une modification au protocole, alors tous les fichiers suivants qui importent mon les classes devront être recompilées.

Quelle est la bonne façon de résoudre ce type de problème?

54
edelaney05

Vous ne pouvez pas transmettre en avant une superclasse ou un protocole auquel elle est conforme. Dans ces cas, vous devez inclure l'en-tête. C'est parce que (dans le cas de la superclasse) les variables et méthodes d'instance de la superclasse font partie de votre classe; ou (dans le cas des protocoles) les méthodes du protocole deviennent des méthodes déclarées dans votre classe, sans avoir besoin de les déclarer explicitement. (C'est-à-dire que maintenant, les autres personnes qui incluent l'en-tête de votre classe verront que votre classe déclare ces méthodes, comme si vous les aviez déclarées vous-même.) La seule manière possible serait qu'elles soient déjà définies dans cette étendue, c'est-à-dire que leurs en-têtes sont importés .

#import <SomeClass.h>
#import <SomeProtocol.h> // these two must be imported

@interface MyClass : SomeClass <SomeProtocol>
@end

Les déclarations directes sont utiles pour les éléments qui apparaissent simplement dans le type d'une variable (en particulier, une variable de pointeur d'objet). Les pointeurs d'objet sont tous de la même taille et il n'y a pas de différence entre les différents types de pointeurs d'objet au moment de l'exécution (le concept de type de pointeur d'objet est juste une chose au moment de la compilation). Il n'y a donc pas vraiment besoin de savoir exactement ce qu'il y a dans les classes de ces types. Ainsi, ils peuvent être déclarés en avant.

@class SomeClass;
@protocol SomeProtocol; // you can forward-declare these

@interface MyClass {
    SomeClass *var1;
    id<SomeProtocol> var2;
}
@end
106
newacct

Oui, vous avez raison, tous les fichiers devront être recompilés, mais cela est nécessaire. Les fichiers qui importent l'en-tête de classe doivent connaître les méthodes associées au protocole qu'il implémente. Si vous cachez cette définition dans le fichier .m, elle n'est visible que dans un seul fichier (car les fichiers .m ne sont jamais importés). Ce n'est pas la même chose que les classes à déclaration directe. Si vous déclarez un protocole en avant, vous devez le déclarer quelque part qui est visible dans la même portée que la déclaration en avant. Je ne peux penser à aucun exemple où cela ne se produit pas dans le même fichier.

Dans ARC, il s'agit d'une erreur, car ARC doit connaître la gestion de la mémoire des méthodes déclarées (renvoient-elles +1 instances, pointeurs internes, etc.?)

4
borrrden

En déclarant votre classe dans MyClass.h, vous devez importer tous les fichiers d'en-tête pour sa superclasse ainsi que les protocoles adoptés dans le fichier MyClass.h. Les protocoles dans Objective-c sont considérés comme un type d'héritage variant.

Les déclarations avancées sont généralement utilisées dans la déclaration de membre de classe.

2
xingzhi.sg

Puisque vous déclarez la conformité au protocole, vous devez #importer l'en-tête contenant le protocole.

C'est la même chose que #importer une superclasse, plutôt que de la déclarer en avant:

@class Foo;

@interface Bar : Foo
// Will get error: attempting to use the forward class 'Foo' as superclass of 'Bar'
@end;
1
Steven Fisher