Question @class vs. # import


Je crois comprendre que l'on devrait utiliser une déclaration de classe avant 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 aussi qu'un #import est un simple ifndef de sorte qu'une inclusion ne se produise qu'une seule fois.

Ma question est la suivante: quand utilise-t-on #import et quand utilise-t-on @class? Parfois, si j'utilise un @class déclaration, je vois un avertissement de compilateur commun comme ce qui suit:

warning: receiver 'FooController' is a forward class and corresponding @interface may not exist.

Voudrais vraiment comprendre cela, par opposition à simplement enlever le @class forward-declaration et lancer un #import pour faire taire les avertissements que le compilateur me donne.


696
2017-11-27 00:20


origine


Réponses:


Si vous voyez cet avertissement:

warning: le destinataire 'MyCoolClass' est une classe forward et l'interface @ correspondante peut ne pas exister

tu dois #import le fichier, mais vous pouvez le faire dans votre fichier d'implémentation (.m), et utilisez le @class déclaration dans votre fichier d'en-tête.

@class ne supprime (habituellement) pas le besoin #import fichiers, il déplace simplement l'exigence plus près de l'endroit où l'information est utile.

Par exemple

Si tu le dis @class MyCoolClass, le compilateur sait qu'il peut voir quelque chose comme:

MyCoolClass *myObject;

Il n'a pas à s'inquiéter de quoi que ce soit d'autre que MyCoolClass est une classe valide, et elle devrait réserver de la place pour un pointeur (vraiment, juste un pointeur). Ainsi, dans votre en-tête, @class suffit 90% du temps.

Cependant, si jamais vous avez besoin de créer ou d'accéder myObjectles membres, vous devez laisser le compilateur savoir quelles sont ces méthodes. À ce stade (probablement dans votre fichier d'implémentation), vous devrez #import "MyCoolClass.h", pour dire au compilateur des informations supplémentaires au-delà de "ceci est une classe".


735
2017-11-27 00:33



Trois règles simples:

  • Seulement #import la super classe, et les protocoles adoptés, dans les fichiers d'en-tête (.h des dossiers).
  • #import toutes les classes et tous les protocoles auxquels vous envoyez des messages en implémentation (.m des dossiers).
  • Transférer les déclarations pour tout le reste.

Si vous faites une déclaration en avant dans les fichiers d'implémentation, alors vous faites probablement quelque chose de mal.


179
2017-08-29 00:34



Regardez la documentation du langage de programmation Objective-C sur ADC

Sous la section Définir une classe | Interface de classe, il décrit pourquoi cela est fait:

La directive @class minimise la quantité de code vue par le compilateur et l'éditeur de liens, et est donc le moyen le plus simple de donner une déclaration avant d'un nom de classe. Étant simple, il évite les problèmes potentiels liés à l'importation de fichiers qui importent encore d'autres fichiers. Par exemple, si une classe déclare une variable d'instance statiquement typée d'une autre classe, et que leurs deux fichiers d'interface s'importent mutuellement, aucune classe ne peut compiler correctement.

J'espère que ça aide.


110
2017-11-27 11:23



Utilisez une déclaration forward 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 avez toujours #import les fichiers que vous utilisez dans votre implémentation, et si vous avez besoin de référencer une classe dans votre fichier d'en-tête, utilisez également une déclaration forward.

le exception à cela est que vous devriez #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).


47
2017-11-27 00:33



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 permettra d'éviter les inclusions circulaires, et cela fonctionne.


24
2017-11-27 01:04



Un autre avantage: la compilation rapide

Si vous incluez un fichier d'en-tête, toute modification entraîne la compilation du fichier courant, mais ce n'est pas le cas si le nom de la classe est inclus @class name. Bien sûr, vous devrez inclure l'en-tête dans le fichier source


24
2017-09-09 08:02



Ma question est la suivante. Quand utilise-t-on #import et quand utilise-t-on @class?

Réponse simple: Vous #import ou #include quand il y a une dépendance physique. Sinon, vous utilisez des déclarations anticipées (@class MONClass, struct MONStruct, @protocol MONProtocol).

Voici quelques exemples courants de dépendance physique:

  • Toute valeur C ou C ++ (un pointeur ou une référence n'est pas une dépendance physique). Si tu as un CGPoint en tant que ivar ou propriété, le compilateur devra voir la déclaration de CGPoint.
  • Votre superclasse.
  • Une méthode que vous utilisez

Parfois, si j'utilise une déclaration @class, je vois un avertissement de compilateur commun tel que:      "warning: le récepteur 'FooController' est une classe forward et l'interface @ correspondante peut ne pas exister."

Le compilateur est vraiment très indulgent à cet égard. Il laissera tomber des indices (comme celui ci-dessus), mais vous pouvez jeter votre pile facilement si vous les ignorez et ne le faites pas #import correctement. Bien qu'il devrait (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 un défaut lorsqu'il rencontre une méthode inconnue que vous appelez. Chaque valeur de retour et chaque paramètre est supposé être id. Ainsi, vous devez éradiquer tous les avertissements de vos bases de code, car cela devrait ê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 être int.

La raison pour laquelle vous privilégieriez les déclarations anticipées est que vous pouvez réduire vos temps de construction par des facteurs car il y a une dépendance minimale. Avec les déclarations forward, 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 quand il n'y a pas de dépendance physique. Les constructions propres prennent moins de temps. Les constructions 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 réduit rapidement les temps de construction (en supposant que votre projet n'est pas minuscule).

Si tu utilises #import ou #include Au lieu de cela, vous lancez beaucoup plus de travail au 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 faites glisser des tonnes d'informations inutiles, ce qui nécessite beaucoup de mémoire, d'E / S de 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 NSObject les types ne sont jamais des valeurs - NSObject les types sont toujours des pointeurs comptés de référence. Vous pouvez donc vous en sortir avec des temps de compilation incroyablement rapides si vous structurez les dépendances de votre programme de manière appropriée et si possible en avant, 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 réduire davantage la dépendance. C'est un énorme bonus pour les grands systèmes - vous connaissez 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 avants si 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 - fixez-les tous. La solution est de #import dans votre fichier d'implémentation.

Lorsque vous créez des bibliothèques, vous allez probablement classer certaines interfaces en tant que groupe, auquel cas vous #import cette bibliothèque où la dépendance physique est introduite (par ex. #import <AppKit/AppKit.h>). Cela peut entraîner une dépendance, mais les responsables de la bibliothèque peuvent souvent gérer les dépendances physiques selon vos besoins - s'ils introduisent une fonctionnalité, ils peuvent minimiser l'impact qu'elle a sur vos builds.


18
2018-02-08 06:10



Je vois beaucoup de "Do it this way" mais je ne vois pas de réponses à "Pourquoi?"

Alors: Pourquoi devriez-vous @class dans votre entête et #import seulement dans votre implémentation? Vous doublez votre travail en ayant à @class et #import tout le temps. Sauf si vous faites usage de 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.

Importer le 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 pratiquement tous les fichiers d'en-tête que nous avons.


11
2018-06-29 15:51



si nous faisons cela

@interface Class_B : Class_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

#import ....
@class Class_A
@interface Class_B

Nous disons ici que nous utilisons le Class_A dans notre programme, mais si nous voulons utiliser les variables Class_A dans Class_B, nous devons #importer le fichier Class_A in .m (faire un objet et utiliser sa fonction et ses variables).


7
2018-01-04 06:32



Pour plus d'informations sur les dépendances de fichiers & #import & @class, procédez comme suit:

http://qualitycoding.org/file-dependencies/ C'est un bon article

résumé de l'article

les importations dans les fichiers d'en-tête:

  • #importer la superclasse dont vous héritez et les protocoles que vous implémentez.
  • Forward-déclare tout le reste (à moins qu'il ne provienne d'un framework   avec un en-tête principal).
  • Essayez d'éliminer tous les autres #imports.
  • Déclarez les protocoles dans leurs propres en-têtes pour réduire les dépendances.
  • Trop de déclarations anticipées? Vous avez une grande classe.

les importations dans les fichiers de mise en œuvre:

  • Éliminer les #portages qui ne sont pas utilisés.
  • Si une méthode délègue à un autre objet et renvoie ce qu'elle obtient   En arrière, essayez de déclarer cet objet au lieu de # l'importer.
  • Si l'inclusion d'un module vous oblige à inclure niveau après niveau de   dépendances successives, vous pouvez avoir un ensemble de classes qui veut   devenir une bibliothèque. Construisez-le comme une bibliothèque séparée avec un maître   en-tête, de sorte que tout peut être amené en un seul bloc pré-construit.
  • Trop d'#imports? Vous avez une grande classe.

5
2017-08-12 11:16



Quand je me développe, je n'ai que trois choses en tête qui ne me posent aucun problème.

  1. Importer des super classes
  2. Importer des classes parentes (lorsque vous avez des enfants et des parents)
  3. Importer des classes en dehors de votre projet (comme dans les frameworks et les bibliothèques)

Pour toutes les autres classes (sous-classes et classes enfants dans mon projet self), je les déclare via forward-class.


3
2018-05-18 15:42