À ma connaissance, il faut utiliser une déclaration de classe directe dans le cas où ClassA doit inclure un en-tête ClassB, et ClassB doit inclure un en-tête ClassA pour éviter toute inclusion circulaire. Je comprends également que an #import
est simple, de ifndef
sorte qu'une inclusion ne se produit qu'une seule fois.
Ma question est la suivante: quand utilise-t-on #import
et quand utilise -t-on @class
? Parfois, si j'utilise une @class
déclaration, je vois un avertissement de compilateur commun tel que le suivant:
warning: receiver 'FooController' is a forward class and corresponding @interface may not exist.
J'adorerais vraiment comprendre cela, par rapport à la simple suppression de la @class
déclaration directe et à la suppression #import
des avertissements que le compilateur me donne.
la source
Réponses:
Si vous voyez cet avertissement:
vous avez besoin
#import
du fichier, mais vous pouvez le faire dans votre fichier d'implémentation (.m) et utiliser la@class
déclaration dans votre fichier d'en-tête.@class
ne supprime pas (généralement) le besoin de#import
fichiers, il déplace simplement l'exigence plus près de l'endroit où les informations sont utiles.Par exemple
Si vous dites
@class MyCoolClass
, le compilateur sait qu'il peut voir quelque chose comme:Il n'a pas à se soucier d'autre chose qu'une
MyCoolClass
classe valide, et il devrait réserver de la place pour un pointeur sur elle (vraiment, juste un pointeur). Ainsi, dans votre en-tête, cela@class
suffit 90% du temps.Cependant, si vous avez besoin de créer ou d'accéder à
myObject
des membres, vous devrez informer le compilateur de ces méthodes. À ce stade (probablement dans votre fichier d'implémentation), vous devrez#import "MyCoolClass.h"
, pour indiquer au compilateur des informations supplémentaires au-delà de "ceci est une classe".la source
@class
quelque chose dans votre.h
fichier, mais oubliez#import
dans le .m, essayez d'accéder à une méthode sur l'@class
objet ed et obtenir des avertissements comme:warning: no -X method found
.Trois règles simples:
#import
la super classe et les protocoles adoptés dans les fichiers d'en-tête (.h
fichiers).#import
toutes les classes et protocoles auxquels vous envoyez des messages dans l'implémentation (.m
fichiers).Si vous transmettez une déclaration dans les fichiers d'implémentation, vous faites probablement quelque chose de mal.
la source
Regardez la documentation du langage de programmation Objective-C sur ADC
Sous la section sur la définition d'une classe | Interface de classe, il explique pourquoi cela est fait:
J'espère que ça aide.
la source
Utilisez une déclaration directe dans le fichier d'en-tête si nécessaire, et
#import
les fichiers d'en-tête pour toutes les classes que vous utilisez dans l'implémentation. En d'autres termes, vous#import
utilisez toujours les fichiers que vous utilisez dans votre implémentation, et si vous devez référencer une classe dans votre fichier d'en-tête, utilisez également une déclaration directe.L' exception à cela est que vous devez
#import
une classe ou un protocole formel dont vous héritez dans votre fichier d'en-tête (auquel cas vous n'auriez pas besoin de l'importer dans l'implémentation).la source
La pratique courante consiste à utiliser @class dans les fichiers d'en-tête (mais vous devez toujours #importer la superclasse) et #import dans les fichiers d'implémentation. Cela évitera toutes les inclusions circulaires, et cela fonctionne simplement.
la source
#import
"est comme la directive #include de C, sauf qu'il s'assure que le même fichier n'est jamais inclus plus d'une fois". Donc, selon cela, il#import
prend en charge les inclusions circulaires, les@class
directives n'y aident pas particulièrement.Autre avantage: compilation rapide
Si vous incluez un fichier d'en-tête, toute modification de celui-ci entraîne également la compilation du fichier actuel, mais ce n'est pas le cas si le nom de la classe est inclus en tant que
@class name
. Bien sûr, vous devrez inclure l'en-tête dans le fichier sourcela source
Réponse simple: vous
#import
ou en#include
cas de dépendance physique. Dans le cas contraire, vous utilisez des déclarations avant (@class MONClass
,struct MONStruct
,@protocol MONProtocol
).Voici quelques exemples courants de dépendance physique:
CGPoint
comme un ivar ou une propriété, le compilateur devra voir la déclaration deCGPoint
.Le compilateur est en fait très indulgent à cet égard. Il laissera tomber des indices (comme celui ci-dessus), mais vous pouvez facilement supprimer votre pile si vous les ignorez et ne le faites pas
#import
correctement. Bien que cela doive (IMO), le compilateur n'applique pas cela. Dans ARC, le compilateur est plus strict car il est responsable du comptage des références. Ce qui se passe, c'est que le compilateur retombe sur une valeur par défaut lorsqu'il rencontre une méthode inconnue que vous appelez. Chaque valeur de retour et paramètre est supposé l'êtreid
. Ainsi, vous devez supprimer tous les avertissements de vos bases de code car cela doit être considéré comme une dépendance physique. Ceci est analogue à l'appel d'une fonction C qui n'est pas déclarée. Avec C, les paramètres sont supposés êtreint
.La raison pour laquelle vous préférez les déclarations avancées est que vous pouvez réduire vos temps de génération par des facteurs car il y a une dépendance minimale. Avec les déclarations avancées, le compilateur voit qu'il y a un nom et peut correctement analyser et compiler le programme sans voir la déclaration de classe ou toutes ses dépendances lorsqu'il n'y a pas de dépendance physique. Les constructions propres prennent moins de temps. Les versions incrémentielles prennent moins de temps. Bien sûr, vous finirez par passer un peu plus de temps à vous assurer que tous les en-têtes dont vous avez besoin sont visibles pour chaque traduction, mais cela se traduit par des temps de construction réduits rapidement (en supposant que votre projet n'est pas minuscule).
Si vous utilisez
#import
ou à la#include
place, vous lancez beaucoup plus de travail sur le compilateur que nécessaire. Vous introduisez également des dépendances d'en-tête complexes. Vous pouvez comparer cela à un algorithme de force brute. Lorsque vous#import
, vous glissez des tonnes d'informations inutiles, ce qui nécessite beaucoup de mémoire, d'E / S disque et de CPU pour analyser et compiler les sources.ObjC est assez proche de l'idéal pour un langage basé sur C en ce qui concerne la dépendance car les
NSObject
types ne sont jamais des valeurs - lesNSObject
types sont toujours des pointeurs comptés par référence. Ainsi, vous pouvez vous en tirer avec des temps de compilation incroyablement rapides si vous structurez les dépendances de votre programme de manière appropriée et si possible, car il y a très peu de dépendance physique requise. Vous pouvez également déclarer des propriétés dans les extensions de classe pour minimiser davantage la dépendance. C'est un énorme bonus pour les grands systèmes - vous sauriez la différence que cela fait si vous avez déjà développé une grande base de code C ++.Par conséquent, ma recommandation est d'utiliser les transferts vers l'avant dans la mesure du possible, puis de
#import
où il y a une dépendance physique. Si vous voyez l'avertissement ou un autre qui implique une dépendance physique - corrigez-les tous. Le correctif se trouve#import
dans votre fichier d'implémentation.Au fur et à mesure que vous construisez des bibliothèques, vous classerez probablement certaines interfaces en tant que groupe, auquel cas vous utiliserez
#import
cette bibliothèque où la dépendance physique est introduite (par exemple#import <AppKit/AppKit.h>
). Cela peut introduire une dépendance, mais les responsables de la bibliothèque peuvent souvent gérer les dépendances physiques pour vous au besoin - s'ils introduisent une fonctionnalité, ils peuvent minimiser l'impact qu'elle a sur vos builds.la source
NSObject types are never values -- NSObject types are always reference counted pointers.
pas entièrement vrai. Les blocs jettent une faille dans votre réponse, juste en disant.Je vois beaucoup de "Faites comme ça" mais je ne vois aucune réponse à "Pourquoi?"
Donc: Pourquoi devriez-vous @class dans votre en-tête et #import uniquement dans votre implémentation? Vous doublez votre travail en devant @class et #import tout le temps. A moins que vous n'utilisiez l'héritage. Dans ce cas, vous importerez plusieurs fois pour une seule @classe. Ensuite, vous devez vous rappeler de supprimer de plusieurs fichiers différents si vous décidez soudainement que vous n'avez plus besoin d'accéder à une déclaration.
L'importation du même fichier plusieurs fois n'est pas un problème en raison de la nature de #import. La compilation des performances n'est pas vraiment un problème non plus. Si c'était le cas, nous n'importerions pas # Cocoa / Cocoa.h ou similaire dans à peu près tous les fichiers d'en-tête que nous avons.
la source
si on fait ça
signifie que nous héritons de Class_A dans Class_B, dans Class_B, nous pouvons accéder à toutes les variables de class_A.
si nous faisons cela
ici, nous disons que nous utilisons Class_A dans notre programme, mais si nous voulons utiliser les variables Class_A dans Class_B, nous devons #importer Class_A dans le fichier .m (créer un objet et utiliser sa fonction et ses variables).
la source
pour plus d'informations sur les dépendances de fichiers et #import & @class, vérifiez ceci:
http://qualitycoding.org/file-dependencies/ bon article
résumé de l'article
la source
Quand je me développe, je n'ai que trois choses en tête qui ne me posent jamais de problème.
Pour toutes les autres classes (sous-classes et classes enfants dans mon projet), je les déclare via forward-class.
la source
Si vous essayez de déclarer une variable ou une propriété dans votre fichier d'en-tête, que vous n'avez pas encore importée, vous obtiendrez une erreur indiquant que le compilateur ne connaît pas cette classe.
Votre première pensée est probablement
#import
celle-ci.Cela peut entraîner des problèmes dans certains cas.
Par exemple, si vous implémentez un tas de méthodes C dans le fichier d'en-tête, ou des structures, ou quelque chose de similaire, car elles ne doivent pas être importées plusieurs fois.
Par conséquent, vous pouvez indiquer au compilateur
@class
:Il dit essentiellement au compilateur de se fermer et de compiler, même s'il n'est pas sûr que cette classe soit jamais implémentée.
Vous utiliserez généralement
#import
dans le .m et@class
dans les .h fichiers.la source
Transférer la déclaration uniquement au compilateur empêchant d'afficher une erreur.
le compilateur saura qu'il y a une classe avec le nom que vous avez utilisé dans votre fichier d'en-tête pour déclarer.
la source
Le compilateur ne se plaindra que si vous allez utiliser cette classe de telle manière que le compilateur ait besoin de connaître son implémentation.
Ex:
Il ne se plaindra pas si vous allez simplement l'utiliser comme pointeur. Bien sûr, vous devrez # l'importer dans le fichier d'implémentation (si vous instanciez un objet de cette classe) car il a besoin de connaître le contenu de la classe pour instancier un objet.
REMARQUE: #import n'est pas identique à #include. Cela signifie qu'il n'y a rien appelé importation circulaire. import est une sorte de demande pour que le compilateur examine un fichier particulier pour obtenir des informations. Si ces informations sont déjà disponibles, le compilateur les ignore.
Essayez simplement cela, importez Ah en Bh et Bh en Ah Il n'y aura pas de problèmes ou de plaintes et cela fonctionnera aussi très bien.
Quand utiliser @class
Vous n'utilisez @class que si vous ne voulez même pas importer un en-tête dans votre en-tête. Cela pourrait être un cas où vous ne vous souciez même pas de savoir quelle sera cette classe. Les cas où vous n'avez peut-être même pas encore d'en-tête pour cette classe.
Un exemple de ceci pourrait être que vous écrivez deux bibliothèques. Une classe, appelons-la A, existe dans une bibliothèque. Cette bibliothèque comprend un en-tête de la deuxième bibliothèque. Cet en-tête peut avoir un pointeur de A mais peut-être pas besoin de l'utiliser à nouveau. Si la bibliothèque 1 n'est pas encore disponible, la bibliothèque B ne sera pas bloquée si vous utilisez @class. Mais si vous cherchez à importer Ah, la progression de la bibliothèque 2 est bloquée.
la source
Considérez @class comme disant au compilateur "croyez-moi, cela existe".
Considérez #import comme un copier-coller.
Vous souhaitez minimiser le nombre d'importations dont vous disposez pour plusieurs raisons. Sans aucune recherche, la première chose qui vient à l'esprit est qu'elle réduit le temps de compilation.
Notez que lorsque vous héritez d'une classe, vous ne pouvez pas simplement utiliser une déclaration directe. Vous devez importer le fichier, afin que la classe que vous déclarez sache comment il est défini.
la source
Ceci est un exemple de scénario, où nous avons besoin de @class.
Considérez si vous souhaitez créer un protocole dans le fichier d'en-tête, qui a un paramètre avec un type de données de la même classe, vous pouvez utiliser @class. N'oubliez pas que vous pouvez également déclarer des protocoles séparément, ce n'est qu'un exemple.
la source