Quelle est la différence entre #import et #include dans Objective-C?

385

Quelles sont les différences entre #import et #include dans Objective-C et y a-t-il des moments où vous devez les utiliser les uns par rapport aux autres? Est-on obsolète?

Je lisais le tutoriel suivant: http://www.otierney.net/objective-c.html#preamble et son paragraphe sur #import et #include semble se contredire ou du moins n'est pas clair.

Ryan Guill
la source

Réponses:

340

La directive #import a été ajoutée à Objective-C en tant que version améliorée de #include. L'amélioration ou non, cependant, fait toujours débat. #import garantit qu'un fichier n'est inclus qu'une seule fois afin que vous n'ayez jamais de problème avec les inclusions récursives. Cependant, la plupart des fichiers d'en-tête décents se protègent de toute façon contre cela, ce n'est donc pas vraiment un avantage.

Fondamentalement, c'est à vous de décider lequel vous souhaitez utiliser. J'ai tendance à #importer les en-têtes pour les choses Objective-C (comme les définitions de classe et autres) et #inclure les éléments C standard dont j'ai besoin. Par exemple, l'un de mes fichiers source pourrait ressembler à ceci:

#import <Foundation/Foundation.h>

#include <asl.h>
#include <mach/mach.h>
Jason Coco
la source
65
Même si les fichiers d'en-tête contiennent des gardes d'inclusion, il y a toujours un impact sur les performances pendant la compilation si vous utilisez #include - le compilateur doit ouvrir chaque fichier d'en-tête pour remarquer les gardes d'inclusion.
Matt Dillard
4
un garde d'en-tête est une directive de préprocesseur qui garantit qu'un en-tête n'est inclus qu'une seule fois dans un fichier source.
Jason Coco, le
8
Je pense que #import est en fait un ajout de GCC, pas d'Objective-C. Vous pouvez l'utiliser dans des langages non ObjC tant que vous compilez avec GCC (ou Clang)
Dave DeLong
34
@dave - #import est un ajout Objective-C au préprocesseur. GCC le supporte également dans les fichiers source C et C ++, bien qu'ils suggèrent officiellement de ne pas l'utiliser en C ou C ++ en faveur de protecteurs d'en-tête traditionnels et portables. Cependant, tous les préprocesseurs Objective-C doivent inclure #import.
Jason Coco
13
Un garde-tête est l'endroit où vous ajoutez en haut: #ifndef myheader #define myheader ... suivi du code d'en-tête ...#endif
Tim
359

Il semble y avoir beaucoup de confusion concernant le préprocesseur.

Ce que fait le compilateur lorsqu'il voit #includequ'il remplace cette ligne par le contenu des fichiers inclus, aucune question n'est posée.

Donc, si vous avez un fichier a.havec ce contenu:

typedef int my_number;

et un fichier b.cavec ce contenu:

#include "a.h"
#include "a.h"

le fichier b.csera traduit par le préprocesseur avant la compilation en

typedef int my_number;
typedef int my_number;

ce qui entraînera une erreur de compilation, car le type my_numberest défini deux fois. Même si la définition est la même, cela n'est pas autorisé par le langage C.

Comme un en-tête est souvent utilisé à plusieurs endroits, les gardes sont généralement utilisés en C. Cela ressemble à ceci:

 #ifndef _a_h_included_
 #define _a_h_included_

 typedef int my_number;

 #endif

Le fichier b.ccontiendrait toujours l'intégralité du contenu de l'en-tête deux fois après avoir été prétraité. Mais la deuxième instance serait ignorée car la macro _a_h_included_aurait déjà été définie.

Cela fonctionne vraiment bien, mais présente deux inconvénients. Tout d'abord, les gardes d'inclusion doivent être écrits et le nom de la macro doit être différent dans chaque en-tête. Et deuxièmement, le compilateur doit toujours chercher le fichier d'en-tête et le lire aussi souvent qu'il est inclus.

Objective-C possède l' #importinstruction de préprocesseur (elle peut également être utilisée pour le code C et C ++ avec certains compilateurs et options). Cela fait presque la même chose #include, mais il note également en interne quel fichier a déjà été inclus. La #importligne est uniquement remplacée par le contenu du fichier nommé pour la première fois qu'il est rencontré. Chaque fois après cela, il est simplement ignoré.

Sven
la source
5
C'est la meilleure réponse que celle acceptée. @Guill, vous devez modifier la réponse acceptée.
Nguyen Minh Binh
6
Après avoir modifié 4 #includes en #imports sur un fichier d'en-tête de modèle de ligne 7000, il y a une amélioration notable des performances dans la compilation et la réactivité XCode intellisense. (Je ne pense pas que je l'imagine)
bobobobo
63

Je suis d'accord avec Jason.

Je me suis fait prendre en faisant ça:

#import <sys/time.h>  // to use gettimeofday() function
#import <time.h>      // to use time() function

Pour GNU gcc, il se plaignait que la fonction time () n'était pas définie.

Alors j'ai changé #import en #include et tout s'est bien passé.

Raison:

Vous #importez <sys / time.h>:
    <sys / time.h> inclut uniquement une partie de <time.h> en utilisant #defines

Vous #importez <time.h>:
    Non, allez. Même si seulement une partie de <time.h> était déjà incluse, en
    ce qui concerne #import, ce fichier est maintenant déjà complètement inclus.

Conclusion:

Les en-têtes C / C ++ incluent traditionnellement des parties d'autres fichiers include.
Donc, pour les en-têtes C / C ++, utilisez #include.
Pour les en-têtes objc / objc ++, utilisez #import.

user512705
la source
2
Il semble que clang n'ait pas ce problème non défini.
ooops
23

#includefonctionne comme le C #include.

#importconserve la trace des en-têtes déjà inclus et est ignoré si un en-tête est importé plusieurs fois dans une unité de compilation. Cela rend inutile l'utilisation de protecteurs d'en-tête.

Le résultat est simplement utilisé #importdans Objective-C et ne vous inquiétez pas si vos en-têtes finissent par importer quelque chose plus d'une fois.

Ferruccio
la source
2
prétendant une minute que je ne connais pas le C #include (principalement parce que je ne le suis pas), quelle est la principale différence entre #include et #import? Pouvez-vous également me dire ce qu'est un protège-tête?
Ryan Guill
@Ryan: Regardez la réponse de Sven.
Adrian Petrescu
13

Je sais que ce fil est vieux ... mais dans "les temps modernes" .. il y a une "stratégie d'inclusion" bien supérieure via les @importmodules de clang - qui est souvent négligée ..

Les modules améliorent l'accès à l'API des bibliothèques de logiciels en remplaçant le modèle d'inclusion textuel du préprocesseur par un modèle sémantique plus robuste et plus efficace. Du point de vue de l'utilisateur, le code n'est que légèrement différent, car on utilise une déclaration d'importation plutôt qu'une directive de préprocesseur #include:

@import Darwin; // Like including all of /usr/include. @see /usr/include/module.map

ou

@import Foundation;  //  Like #import <Foundation/Foundation.h>
@import ObjectiveC;  //  Like #import <objc/runtime.h>

Cependant, cette importation de module se comporte assez différemment de la #include correspondante: lorsque le compilateur voit l'importation de module ci-dessus, il charge une représentation binaire du module et met son API à la disposition de l'application directement. Les définitions de préprocesseur qui précèdent la déclaration d'importation n'ont aucun impact sur l'API fournie ... car le module lui-même a été compilé en tant que module autonome distinct. De plus, tout indicateur de l'éditeur de liens requis pour utiliser le module sera automatiquement fourni lors de l'importation du module. Ce modèle d'importation sémantique résout de nombreux problèmes du modèle d'inclusion de préprocesseur.

Pour activer les modules, passez l'indicateur de ligne de commande -fmodulesaka CLANG_ENABLE_MODULESin Xcode- au moment de la compilation. Comme mentionné ci-dessus .. cette stratégie évite TOUT et TOUS LDFLAGS. Comme dans, vous pouvez SUPPRIMER tous les paramètres "OTHER_LDFLAGS", ainsi que toutes les phases de "liaison".

entrez la description de l'image ici

Je trouve que les temps de compilation / lancement se "sentent" beaucoup plus rapides (ou peut-être, il y a juste un peu moins de retard lors de la "liaison"?) .. et fournit également une excellente occasion de purger le fichier Project-Prefix.pch désormais superflu, et paramètres de construction correspondants, GCC_INCREASE_PRECOMPILED_HEADER_SHARING, GCC_PRECOMPILE_PREFIX_HEADERet GCC_PREFIX_HEADER, etc.

De plus, bien que cela ne soit pas bien documenté… Vous pouvez créer des module.maps pour vos propres frameworks et les inclure de la même manière pratique. Vous pouvez jeter un oeil à mon repo github ObjC-Clang-Modules pour quelques exemples de mise en œuvre de tels miracles.

Alex Gray
la source
4

Si vous connaissez C ++ et les macros, alors

#import "Class.h" 

est similaire à

{
#pragma once

#include "class.h"
}

ce qui signifie que votre classe ne sera chargée qu'une seule fois lors de l'exécution de votre application.

Evol Gate
la source
Est-ce une utilisation prise en charge de #pragma une fois? J'ai toujours pensé le pragma devait être à l' intérieur du includ ed fichier au travail.
uliwitness
@uliwitness Vous avez raison. #pragma onceest placé dans le fichier inclus, pas le fichier qui effectue l'inclusion. -1 pour ça.
herzbube
1

Dans le cas où j'ai eu une variable globale dans l'un de mes .hfichiers qui causait le problème, et je l'ai résolu en l'ajoutant externdevant.

neowinston
la source
0

SI vous #incluez un fichier deux fois dans des fichiers .h, le compilateur donnera une erreur. Mais si vous # importez un fichier plus d'une fois, le compilateur l'ignorera.

Husmukh
la source
8
#includele même fichier deux fois n'entraîne pas d'erreur.
kennytm
1
Pour compléter le commentaire de @ KennyTM, # l'inclusion du même fichier deux fois dans le même en-tête n'entraîne pas d'erreur de compilation SI les gardes d'en-tête habituels (#ifndef FILE_NAME_H #define FILE_NAME_H #end) sont là. C'est une pratique attendue. En utilisant #import, les protections d'en-tête ne sont pas nécessaires.
jbat100
@ jbat100: #includeest simplement un mécanisme de copier-coller. Il y a une utilisation délibérée de #includeplusieurs fois sans inclure de gardes, par exemple la "macro X".
kennytm
L'inclusion d'un fichier deux fois peut entraîner des erreurs selon ce que vous incluez. J'ai vu du code C qui #includeimplémentait une sorte de modèles. Ils ont fait un #define, inclus un en-tête, #undefd et refait le #define, inclus le même en-tête une deuxième fois. Cela a abouti à ce que le code soit paramétré, valide et inclus deux fois, car la valeur de la définition était différente. Il y a donc des avantages à utiliser #include, mais si vous utilisez un langage moderne comme C ++ ou ObjC, vous n'avez généralement pas besoin de cela.
uliwitness
0

#includeil sert à obtenir des "choses" d'un autre fichier à celui #includedans lequel il est utilisé. Ex:

dans le fichier: main.cpp

#include "otherfile.h"

// some stuff here using otherfile.h objects,
// functions or classes declared inside

Le protège-en-tête est utilisé en haut de chaque fichier d'en-tête (* .h) pour éviter d'inclure le même fichier plus d'une fois (si cela se produit, vous obtiendrez des erreurs de compilation).

dans le fichier: otherfile.h

#ifndef OTHERFILE
#define OTHERFILE

// declare functions, classes or objects here

#endif

même si vous mettez #include"otherfile.h" n time dans votre code, celui-ci ne sera pas redéclaré.

Celso Dantas
la source
0
#include + guard == #import

#include guardWiki - macro guard, header guard ou file guard empêche de doubler un en-tête par unpreprocessorqui peut ralentir un temps de construction

La prochaine étape est

.pch[À propos] =>@import [À propos]

[#import dans .hou .m]

yoAlex5
la source