¿Cuál es la diferencia entre #import y #include en Objective-C?

Respuestas:

340

La directiva #import se agregó a Objective-C como una versión mejorada de #include. Sin embargo, si se mejora o no, aún es un tema de debate. #import asegura que un archivo solo se incluya una vez para que nunca tenga problemas con las inclusiones recursivas. Sin embargo, la mayoría de los archivos de encabezado decentes se protegen contra esto de todos modos, por lo que no es realmente un gran beneficio.

Básicamente, depende de usted decidir cuál quiere usar. Tiendo a # importar encabezados para cosas de Objective-C (como definiciones de clase y tal) e # incluir cosas estándar de C que necesito. Por ejemplo, uno de mis archivos fuente podría verse así:

#import <Foundation/Foundation.h>

#include <asl.h>
#include <mach/mach.h>
Jason Coco
fuente
65
Incluso si los archivos de encabezado contienen protectores de inclusión, todavía hay un impacto en el rendimiento durante la compilación si usa #include: el compilador debe abrir cada archivo de encabezado para notar los protectores de inclusión.
Matt Dillard el
44
un protector de encabezado es una directiva de preprocesador que garantiza que un encabezado solo se incluya una vez en un archivo fuente.
Jason Coco
8
Creo que #import es en realidad una adición de GCC, no de Objective-C. Puede usarlo en lenguajes que no sean ObjC siempre que compile con GCC (o Clang)
Dave DeLong
34
@dave - #import es una adición de Objective-C al preprocesador. GCC solo lo admite en archivos fuente C y C ++ también, aunque oficialmente sugieren no usarlo en C o C ++ en favor de protectores de encabezado portátiles y tradicionales. Sin embargo, todos los preprocesadores de Objective-C deben incluir #import.
Jason Coco
13
Un encabezado es donde se agrega a la parte superior: #ifndef myheader #define myheader ... seguido del código del encabezado ...#endif
Tim
359

Parece haber mucha confusión con respecto al preprocesador.

Lo que hace el compilador cuando ve #includeque reemplaza esa línea con el contenido de los archivos incluidos, sin hacer preguntas.

Entonces, si tiene un archivo a.hcon este contenido:

typedef int my_number;

y un archivo b.ccon este contenido:

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

el archivo b.cserá traducido por el preprocesador antes de la compilación a

typedef int my_number;
typedef int my_number;

que se traducirá en un error de compilación, ya que el tipo my_numberse define dos veces. Aunque la definición es la misma, el lenguaje C no lo permite.

Dado que un encabezado a menudo se usa en más de un lugar, los guardias de inclusión generalmente se usan en C.

 #ifndef _a_h_included_
 #define _a_h_included_

 typedef int my_number;

 #endif

El archivo b.caún tendría todo el contenido del encabezado en él dos veces después de haber sido preprocesado. Pero la segunda instancia sería ignorada ya que la macro _a_h_included_ya habría sido definida.

Esto funciona muy bien, pero tiene dos inconvenientes. En primer lugar, los guardias de inclusión deben escribirse, y el nombre de la macro debe ser diferente en cada encabezado. Y en segundo lugar, el compilador todavía tiene que buscar el archivo de encabezado y leerlo con tanta frecuencia como está incluido.

Objective-C tiene la #importinstrucción de preprocesador (también se puede usar para código C y C ++ con algunos compiladores y opciones). Esto hace casi lo mismo que #include, pero también observa internamente qué archivo ya se ha incluido. La #importlínea solo se reemplaza por el contenido del archivo con nombre por primera vez. Cada vez después de eso simplemente se ignora.

Sven
fuente
55
Esta es la mejor respuesta que la aceptada. @Guill, debes cambiar la respuesta aceptada.
Nguyen Minh Binh
66
Después de cambiar 4 #includesa #imports en un archivo de encabezado de plantilla de línea 7000, hay una mejora notable en el rendimiento de la compilación y la capacidad de respuesta intellisense de XCode. (No creo que lo esté imaginando)
bobobobo
63

Estoy de acuerdo con Jason

Me pillaron haciendo esto:

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

Para GNU gcc, seguía quejándose de que la función time () no estaba definida.

Entonces cambié #importar a #include y todo salió bien.

Razón:

# Importa <sys / time.h>:
    <sys / time.h> incluye solo una parte de <time.h> utilizando #defines

# Importas <time.h>:
    No te vayas. Aunque solo una parte de <time.h> ya estaba incluida, en
    lo que respecta a #import, ese archivo ya está completamente incluido.

Línea de fondo:

Los encabezados C / C ++ tradicionalmente incluyen partes de otros archivos de inclusión.
Entonces, para los encabezados C / C ++, use #include.
Para los encabezados objc / objc ++, use #import.

user512705
fuente
2
Parece que el sonido metálico no tiene este problema no definido.
ooops
23

#includefunciona igual que el C #include.

#importrealiza un seguimiento de qué encabezados ya se han incluido y se ignora si un encabezado se importa más de una vez en una unidad de compilación. Esto hace innecesario el uso de protectores de encabezado.

La conclusión es solo usar #importen Objective-C y no te preocupes si tus encabezados terminan importando algo más de una vez.

Ferruccio
fuente
2
pretendiendo por un minuto que no estoy familiarizado con el C #include (principalmente porque no lo estoy), ¿cuál es la principal diferencia entre #include y #import? Además, ¿puedes decirme qué es un protector de cabecera?
Ryan Guill
@ Ryan: Mira la respuesta de Sven.
Adrian Petrescu
13

Sé que este hilo es antiguo ... pero en los "tiempos modernos" ... hay una "estrategia de inclusión" muy superior a través de los @importmódulos de clang, que a menudo se pasa por alto ...

Los módulos mejoran el acceso a la API de las bibliotecas de software al reemplazar el modelo de inclusión de preprocesador textual con un modelo semántico más robusto y eficiente. Desde la perspectiva del usuario, el código se ve solo ligeramente diferente, porque uno usa una declaración de importación en lugar de una directiva #include preprocesador:

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

o

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

Sin embargo, la importación de este módulo se comporta de manera bastante diferente del #include correspondiente: cuando el compilador ve la importación del módulo anterior, carga una representación binaria del módulo y pone su API a disposición de la aplicación directamente. Las definiciones de preprocesador que preceden a la declaración de importación no tienen ningún impacto en la API proporcionada ... porque el módulo en sí se compiló como un módulo independiente y separado. Además, cualquier indicador de enlazador requerido para usar el módulo se proporcionará automáticamente cuando se importe el módulo. Este modelo de importación semántico aborda muchos de los problemas del modelo de inclusión del preprocesador.

Para habilitar los módulos, pase el indicador de línea de comandos -fmodulestambién conocido CLANG_ENABLE_MODULEScomo Xcode- en el momento de la compilación. Como se mencionó anteriormente ... esta estrategia obvia CUALQUIERA y TODO LDFLAGS. Como en, puede ELIMINAR cualquier configuración de "OTHER_LDFLAGS", así como también cualquier fase de "Vinculación".

ingrese la descripción de la imagen aquí

Encuentro que los tiempos de compilación / lanzamiento se "sienten" mucho más ágiles (o posiblemente, ¿hay un retraso menor durante la "vinculación"?) Y, además, brinda una gran oportunidad para purgar el archivo Project-Prefix.pch, ahora extraño, y configuración de generación correspondiente GCC_INCREASE_PRECOMPILED_HEADER_SHARING, GCC_PRECOMPILE_PREFIX_HEADERy GCC_PREFIX_HEADER, etc.

Además, aunque no está bien documentado ... Puede crear correos module.mapelectrónicos para sus propios marcos e incluirlos de la misma manera conveniente. Puede echar un vistazo a mi repositorio github de ObjC-Clang-Modules para ver algunos ejemplos de cómo implementar tales milagros.

Alex Gray
fuente
4

Si está familiarizado con C ++ y macros, entonces

#import "Class.h" 

es parecido a

{
#pragma once

#include "class.h"
}

lo que significa que su clase se cargará solo una vez cuando se ejecute su aplicación.

Puerta Evol
fuente
¿Es este un uso compatible de #pragma una vez? Siempre pensé que el pragma tenía que ser dentro de la includ ed archivo de trabajo.
uliwitness
@uliwitness Tienes razón. #pragma oncese coloca en el archivo incluido, no en el archivo que realiza la inclusión. -1 por eso.
herzbube
1

En muchos casos, tenía una variable global en uno de mis .harchivos que causaba el problema, y ​​la resolví agregando externfrente a ella.

Neowinston
fuente
0

SI #incluye un archivo dos veces en archivos .h, el compilador dará un error. Pero si #importa un archivo más de una vez, el compilador lo ignorará.

Husmukh
fuente
8
#includeel mismo archivo dos veces no produce un error.
kennytm
1
Para complementar el comentario de @ KennyTM, # incluir el mismo archivo dos veces en el mismo encabezado no genera un error de compilación SI las protecciones de encabezado habituales (#ifndef FILE_NAME_H #define FILE_NAME_H #end) están ahí. Esta es la práctica esperada. Usando #import los encabezados no son necesarios.
jbat100
@ jbat100: #includees simplemente un mecanismo de copiar y pegar. Hay un uso deliberado de #includemás de una vez sin incluir guardias, por ejemplo, la "macro X".
kennytm
La inclusión de un archivo dos veces puede generar errores dependiendo de lo que incluya. He visto código C que solía #includeimplementar un tipo de plantillas. Hicieron a #define, incluyeron un encabezado, #undefd y rehicieron el #define, incluyeron el mismo encabezado por segunda vez. Esto dio como resultado que el código fuera parametrizado, válido e incluido dos veces, ya que el valor de la definición era diferente. Por lo tanto, el uso tiene ventajas #include, pero si usa un lenguaje moderno como C ++ u ObjC, generalmente no necesita esto.
uliwitness
0

#includesolía llevar "cosas" de otro archivo al que #includese usa. Ej:

en el archivo: main.cpp

#include "otherfile.h"

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

El protector de encabezado se usa en la parte superior de cada archivo de encabezado (* .h) para evitar incluir el mismo archivo más de una vez (si sucede, obtendrá errores de compilación).

en el archivo: otherfile.h

#ifndef OTHERFILE
#define OTHERFILE

// declare functions, classes or objects here

#endif

incluso si coloca #include"otherfile.h" n tiempo en su código, este no se volverá a declarar.

Celso Dantas
fuente
0
#include + guard == #import

#include guardWiki : la protección de macros, la protección de encabezado o la protección de archivos evita que se incluya un encabezado doblepreprocessorque puede ralentizar el tiempo de compilación

El siguiente paso es

.pch[Acerca de] =>@import [Acerca de]

[# importación en .ho .m]

yoAlex5
fuente