Qual é a diferença entre #import e #include no Objective-C?

385

Quais são as diferenças entre #import e #include no Objective-C e há momentos em que você deve usar um sobre o outro? Alguém está obsoleto?

Eu estava lendo o seguinte tutorial: http://www.otierney.net/objective-c.html#preamble e seu parágrafo sobre #import e #include parece se contradizer ou pelo menos não está claro.

Ryan Guill
fonte

Respostas:

340

A diretiva #import foi adicionada ao Objective-C como uma versão aprimorada do #include. Se é ou não melhorado, no entanto, ainda é uma questão de debate. #import garante que um arquivo seja incluído apenas uma vez, para que você nunca tenha problemas com inclusões recursivas. No entanto, a maioria dos arquivos de cabeçalho decentes se protege contra isso de qualquer maneira, portanto, não é realmente um grande benefício.

Basicamente, cabe a você decidir qual você deseja usar. Costumo # importar cabeçalhos para coisas de Objective-C (como definições de classe e outras) e # incluir coisas padrão de C que eu preciso. Por exemplo, um dos meus arquivos de origem pode ficar assim:

#import <Foundation/Foundation.h>

#include <asl.h>
#include <mach/mach.h>
Jason Coco
fonte
65
Mesmo que os arquivos de cabeçalho contenham proteções de inclusão, ainda haverá um impacto no desempenho durante a compilação se você usar #include - o compilador deve abrir cada arquivo de cabeçalho para observar as proteções de inclusão.
Matt Dillard
4
uma proteção de cabeçalho é uma diretiva de pré-processador que garante que um cabeçalho seja incluído apenas uma vez em um arquivo de origem.
2113 Jason Jason as
8
Acho que #import é realmente uma adição do GCC, não do Objective-C. Você pode usá-lo em línguas não ObjC contanto que você compilar com GCC (ou Clang)
Dave DeLong
34
@dave - #import é uma adição de Objective-C ao pré-processador. O GCC também suporta apenas arquivos de origem C e C ++, apesar de sugerirem oficialmente não usá-lo em C ou C ++ em favor de guardas de cabeçalho portáteis e tradicionais. Todos os pré-processadores Objective-C devem incluir #import, no entanto.
2113 Jason Jason
13
Um guarda de cabeçalho é onde você adiciona ao topo: #ifndef myheader #define myheader ... seguido por código de cabeçalho ...#endif
Tim
359

Parece haver muita confusão em relação ao pré-processador.

O que o compilador faz quando vê #includeque substitui essa linha pelo conteúdo dos arquivos incluídos, sem perguntas.

Portanto, se você possui um arquivo a.hcom este conteúdo:

typedef int my_number;

e um arquivo b.ccom este conteúdo:

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

o arquivo b.cserá traduzido pelo pré-processador antes da compilação para

typedef int my_number;
typedef int my_number;

o que resultará em um erro do compilador, pois o tipo my_numberé definido duas vezes. Mesmo que a definição seja a mesma, isso não é permitido pela linguagem C.

Como um cabeçalho geralmente é usado em mais de um local, os protetores geralmente são usados ​​em C. Isso se parece com isso:

 #ifndef _a_h_included_
 #define _a_h_included_

 typedef int my_number;

 #endif

O arquivo b.cainda teria todo o conteúdo do cabeçalho nele duas vezes depois de pré-processado. Mas a segunda instância seria ignorada, pois a macro _a_h_included_já teria sido definida.

Isso funciona muito bem, mas tem duas desvantagens. Antes de tudo, os protetores de inclusão precisam ser escritos e o nome da macro deve ser diferente em cada cabeçalho. Em segundo lugar, o compilador ainda precisa procurar o arquivo de cabeçalho e lê-lo com a frequência incluída.

O Objective-C possui a #importinstrução de pré - processador (também pode ser usada para o código C e C ++ com alguns compiladores e opções). Isso faz quase o mesmo que #include, mas também observa internamente qual arquivo já foi incluído. A #importlinha é substituída apenas pelo conteúdo do arquivo nomeado pela primeira vez. Toda vez depois disso é apenas ignorado.

Sven
fonte
5
Esta é a melhor resposta do que a aceita. @ Guill, você deve alterar a resposta aceita.
Nguyen Minh Binh
6
Depois de alterar 4 #includes para #imports em um arquivo de cabeçalho de modelo de linha 7000, há uma melhoria notável no desempenho na compilação e na capacidade de resposta do XCode intellisense. (Eu não acho que eu estou imaginando)
bobobobo
63

Eu concordo com o Jason.

Eu fui pego fazendo isso:

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

Para o GNU gcc, ele continuava reclamando que a função time () não estava definida.

Então mudei #import para #include e tudo correu bem.

Razão:

Você #importa <sys / time.h>:
    <sys / time.h> inclui apenas uma parte de <time.h> usando #defines

Você # importa <time.h>:
    não é possível. Embora apenas parte de <time.h> já tenha sido incluída, no que
    diz respeito a #import, esse arquivo já está completamente incluído.

Bottom line:

Tradicionalmente, os cabeçalhos C / C ++ incluem partes de outros arquivos de inclusão.
Portanto, para cabeçalhos C / C ++, use #include.
Para cabeçalhos objc / objc ++, use #import.

user512705
fonte
2
Parece que o clang não tem esse problema não definido.
ooops 23/02
23

#includefunciona exatamente como o C #include.

#importcontrola quais cabeçalhos já foram incluídos e será ignorado se um cabeçalho for importado mais de uma vez em uma unidade de compilação. Isso torna desnecessário o uso de proteções de cabeçalho.

A linha inferior é apenas usar #importno Objective-C e não se preocupe se seus cabeçalhos acabarem importando algo mais de uma vez.

Ferruccio
fonte
2
fingindo por um minuto que não estou familiarizado com o C #include (principalmente porque não o sou), qual é a principal diferença entre #include e #import? Além disso, você pode me dizer o que é um protetor de cabeçalho?
22690 Ryan Guill
@ Ryan: Veja a resposta de Sven.
Adrian Petrescu
13

Eu sei que esse tópico é antigo ... mas nos "tempos modernos" .. há uma estratégia de inclusão "muito superior" através dos @importmódulos do clang - que é frequentemente ignorada.

Os módulos aprimoram o acesso à API das bibliotecas de software, substituindo o modelo de inclusão de pré-processador de texto por um modelo semântico mais robusto e eficiente. Da perspectiva do usuário, o código parece um pouco diferente, porque se usa uma declaração de importação em vez de uma diretiva #include pré-processador:

@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>

No entanto, essa importação de módulo se comporta de maneira bastante diferente da #include correspondente: quando o compilador vê a importação de módulo acima, ele carrega uma representação binária do módulo e disponibiliza sua API diretamente para o aplicativo. As definições de pré-processador que precedem a declaração de importação não têm impacto na API fornecida ... porque o próprio módulo foi compilado como um módulo independente e separado. Além disso, quaisquer sinalizadores de vinculador necessários para usar o módulo serão automaticamente fornecidos quando o módulo for importado. Esse modelo de importação semântica aborda muitos dos problemas do modelo de inclusão de pré-processador.

Para habilitar os módulos, passe o sinalizador da linha de comando -fmodulesaka CLANG_ENABLE_MODULESin Xcode- em tempo de compilação. Como mencionado acima .. essa estratégia evita QUALQUER e TODOS LDFLAGS. Assim, você pode REMOVER qualquer configuração "OTHER_LDFLAGS", bem como qualquer fase "Vincular".

insira a descrição da imagem aqui

Acho que os tempos de compilação / inicialização "parecem" muito mais rápidos (ou possivelmente há menos atraso ao "vincular"?) ... e também oferecem uma ótima oportunidade para limpar o arquivo Project-Prefix.pch, agora estranho, e configurações de compilação correspondente GCC_INCREASE_PRECOMPILED_HEADER_SHARING, GCC_PRECOMPILE_PREFIX_HEADERe GCC_PREFIX_HEADER, etc.

Além disso, embora não esteja bem documentado ... Você pode criar module.maps para suas próprias estruturas e incluí-las da mesma maneira conveniente. Você pode dar uma olhada no meu repositório github ObjC-Clang-Modules para ver alguns exemplos de como implementar esses milagres.

Alex Gray
fonte
4

Se você estiver familiarizado com C ++ e macros, então

#import "Class.h" 

é similar a

{
#pragma once

#include "class.h"
}

o que significa que sua classe será carregada apenas uma vez quando o aplicativo for executado.

Evol Gate
fonte
Esse é um uso suportado do #pragma uma vez? Eu sempre achei que o pragma precisava estar dentro da inclu ed arquivo para o trabalho.
uliwitness
@uliwitness Você está correto. #pragma onceé colocado no arquivo incluído, não no arquivo que executa a inclusão. -1 para isso.
herzbube
1

Em maio, eu tinha uma variável global em um dos meus .harquivos que estava causando o problema e a resolvi adicionando-a externà frente.

neowinston
fonte
0

Se você # incluir um arquivo duas vezes em arquivos .h, o compilador dará erro. Mas se você # importar um arquivo mais de uma vez, o compilador o ignorará.

Husmukh
fonte
8
#includeo mesmo arquivo duas vezes não resulta em erro.
Kennytm
11
Para complemento de @ KennyTM comentário, # incluem-ing o mesmo arquivo duas vezes no mesmo cabeçalho não resultar em um erro de compilação se a gards cabeçalho habituais (#ifndef FILE_NAME_H #define FILE_NAME_H #end) estão lá. Esta é uma prática esperada. Usando #import, os protetores de cabeçalho não são necessários.
perfil completo de jbat100
@ jbat100: #includeé simplesmente um mecanismo de copiar e colar. Há uso deliberado de #includemais de uma vez sem incluir proteções, por exemplo, a "macro X".
Kennytm
A inclusão de um arquivo duas vezes pode resultar em erros, dependendo do que você incluir. Eu vi o código C que costumava #includeimplementar um tipo de modelos. Eles fizeram a #define, incluíram um cabeçalho, #undefrefizeram o #define, incluíram o mesmo cabeçalho uma segunda vez. Isso resultou no código sendo parametrizado, válido e incluído duas vezes, pois o valor da definição era diferente. Portanto, há vantagens em usar #include, mas se você estiver usando uma linguagem moderna como C ++ ou ObjC, geralmente não precisará disso.
uliwitness
0

#includecostumava obter "coisas" de outro arquivo para aquele em que #includeé usado. Ex:

no arquivo: main.cpp

#include "otherfile.h"

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

A proteção de cabeçalho é usada na parte superior de cada arquivo de cabeçalho (* .h) para impedir a inclusão do mesmo arquivo mais de uma vez (se isso acontecer, você receberá erros de compilação).

no arquivo: otherfile.h

#ifndef OTHERFILE
#define OTHERFILE

// declare functions, classes or objects here

#endif

mesmo se você colocar #include"otherfile.h" n tempo no seu código, isso dentro dele não será redeclarado.

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

#include guardWiki - proteção de macro, proteção de cabeçalho ou proteção de arquivo impede a inclusão dupla de um cabeçalho por umpreprocessorque pode diminuir o tempo de compilação

O próximo passo é

.pch[Sobre] =>@import [Sobre]

[#importar .hou .m]

yoAlex5
fonte