Jaka jest różnica między #import i #include w Objective-C?


Odpowiedzi:


341

Dyrektywa #import została dodana do Objective-C jako ulepszona wersja #include. Jednak kwestia, czy jest poprawiona, jest nadal kwestią dyskusyjną. #import zapewnia, że ​​plik jest dołączany tylko raz, więc nigdy nie będziesz mieć problemu z rekurencyjnymi włączeniami. Jednak większość porządnych plików nagłówkowych i tak się przed tym chroni, więc nie jest to tak bardzo korzystne.

Zasadniczo to Ty decydujesz, którego chcesz użyć. Staram się #importować nagłówki do rzeczy w Objective-C (takich jak definicje klas i tym podobne) i #include standardowych rzeczy C, których potrzebuję. Na przykład jeden z moich plików źródłowych może wyglądać następująco:

#import <Foundation/Foundation.h>

#include <asl.h>
#include <mach/mach.h>

63
Nawet jeśli pliki nagłówkowe zawierają osłony włączające, podczas kompilacji nadal występuje spadek wydajności, jeśli używasz #include - kompilator musi otworzyć każdy plik nagłówkowy, aby zauważyć osłony dołączające.
Matt Dillard

4
osłona nagłówka to dyrektywa preprocesora, która zapewnia, że ​​nagłówek jest dołączany tylko raz w pliku źródłowym.
Jason Coco,

8
Myślę, że #import jest tak naprawdę dodatkiem GCC, a nie Objective-C. Możesz go używać w językach innych niż ObjC, o ile kompilujesz z GCC (lub Clang)
Dave DeLong,

33
@dave - #import jest dodatkiem Objective-C do preprocesora. GCC po prostu obsługuje go również w plikach źródłowych C i C ++, choć oficjalnie sugerują, aby nie używać go w C lub C ++ na rzecz przenośnych, tradycyjnych osłon nagłówków. Wszystkie preprocesory Objective-C muszą jednak zawierać #import.
Jason Coco,

13
Strażnik nagłówka to miejsce, w którym dodajesz na górę: #ifndef myheader #define myheader ... a następnie kod nagłówka ...#endif
Tim

358

Wydaje się, że istnieje wiele nieporozumień dotyczących preprocesora.

Co robi kompilator, gdy zobaczy #include, że zastępuje ten wiersz zawartością dołączonych plików, bez zadawania pytań.

Więc jeśli masz plik a.hz tą zawartością:

typedef int my_number;

i plik b.cz tą zawartością:

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

plik b.czostanie przetłumaczony przez preprocesora przed kompilacją

typedef int my_number;
typedef int my_number;

co spowoduje błąd kompilatora, ponieważ typ my_numberjest zdefiniowany dwukrotnie. Mimo że definicja jest taka sama, język C nie zezwala na to.

Ponieważ nagłówek jest często używany w więcej niż jednym miejscu, w C. zwykle stosowane są osłony. Wygląda to tak:

 #ifndef _a_h_included_
 #define _a_h_included_

 typedef int my_number;

 #endif

Plik b.cnadal zawierałby całą zawartość nagłówka dwukrotnie po przetworzeniu. Ale druga instancja zostałaby zignorowana, ponieważ makro _a_h_included_zostałoby już zdefiniowane.

Działa to naprawdę dobrze, ale ma dwie wady. Przede wszystkim należy napisać strażników dołączania, a nazwa makra musi być inna w każdym nagłówku. Po drugie, kompilator wciąż musi szukać pliku nagłówka i czytać go tak często, jak jest dołączony.

Objective-C ma #importinstrukcję preprocesora (może być również używana do kodu C i C ++ z niektórymi kompilatorami i opcjami). Robi to prawie tak samo jak #include, ale wewnętrznie zauważa, który plik został już dołączony. #importLinia otrzymuje jedynie przez zawartość pliku o nazwie po raz pierwszy go spotkałem. Za każdym razem jest to po prostu ignorowane.


4
To jest lepsza odpowiedź niż zaakceptowana. @ Guill, powinieneś zmienić zaakceptowaną odpowiedź.
Nguyen Minh Binh

5
Po zmianie 4 #includes #importna plik nagłówka szablonu linii 7000 zauważalna jest poprawa wydajności kompilacji i responsywności inteligencji XCode. (Nie sądzę, że to sobie wyobrażam)
bobobobo

62

Zgadzam się z Jasonem.

Zostałem przyłapany na robieniu tego:

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

W przypadku GNU gcc ciągle narzekał, że funkcja time () nie została zdefiniowana.

Więc zmieniłem #import na #include i wszystko poszło dobrze.

Powód:

#Import <sys / time.h>:
    <sys / time.h> obejmuje tylko część <time.h> przy użyciu #defines

Ty #import <time.h>:
    Nie idź. Mimo że tylko część <time.h> została już uwzględniona, jeśli
    chodzi o #import, plik ten jest już całkowicie uwzględniony.

Dolna linia:

Nagłówki C / C ++ tradycyjnie zawierają części innych plików dołączanych.
Dlatego w przypadku nagłówków C / C ++ użyj #include.
W przypadku nagłówków objc / objc ++ użyj #import.


1
Wygląda na to, że clang nie ma tego nieokreślonego problemu.
ooops

23

#includedziała podobnie jak C #include.

#importśledzi, które nagłówki zostały już uwzględnione i jest ignorowany, jeśli nagłówek jest importowany więcej niż raz w jednostce kompilacyjnej. To sprawia, że ​​nie trzeba używać osłon nagłówka.

Najważniejsze jest użycie #importw Objective-C i nie martw się, jeśli twoje nagłówki skończą importować coś więcej niż raz.


2
udając przez minutę, że nie znam C #include (głównie dlatego, że nie jestem), jaka jest główna różnica między #include i #import? Czy możesz mi powiedzieć, co to jest osłona nagłówka?
Ryan Guill

@Ryan: Spójrz na odpowiedź Svena.
Adrian Petrescu,

13

Wiem, że ten wątek jest stary ... ale w „czasach współczesnych” .. istnieje moduły clanga@import o wiele lepsze niż „strategia” - to często pomijane…

Moduły poprawiają dostęp do interfejsu API bibliotek oprogramowania, zastępując tekstowy model włączenia preprocesora bardziej solidnym, wydajnym modelem semantycznym. Z punktu widzenia użytkownika kod wygląda tylko nieco inaczej, ponieważ używa się deklaracji importu, a nie dyrektywy preprocesora #include:

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

lub

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

Jednak ten import modułu zachowuje się zupełnie inaczej niż odpowiedni #include: gdy kompilator zobaczy powyższy import modułu, ładuje binarną reprezentację modułu i udostępnia bezpośrednio interfejs API aplikacji. Definicje preprocesora poprzedzające deklarację importu nie mają wpływu na udostępniony interfejs API ... ponieważ sam moduł został skompilowany jako osobny, samodzielny moduł. Ponadto wszelkie flagi linkera wymagane do korzystania z modułu będą automatycznie dostarczane po zaimportowaniu modułu. Ten semantyczny model importu rozwiązuje wiele problemów modelu włączenia preprocesora.

Aby włączyć moduły, przekaż flagę wiersza polecenia -fmodulesaka CLANG_ENABLE_MODULESin Xcode- w czasie kompilacji. Jak wspomniano powyżej ... ta strategia eliminuje WSZYSTKIE i WSZYSTKIE LDFLAGS. W tej chwili możesz USUNĄĆ wszelkie ustawienia „OTHER_LDFLAGS”, a także wszelkie fazy „Łączenie”.

wprowadź opis zdjęcia tutaj

Uważam, że czasy kompilacji / uruchamiania „czują się” znacznie szybsze (a może po prostu mniej opóźnień podczas „łączenia”?) .., a także, stanowią doskonałą okazję do wyczyszczenia obcego pliku Project-Prefix.pch, i odpowiadających ustawień budować GCC_INCREASE_PRECOMPILED_HEADER_SHARING, GCC_PRECOMPILE_PREFIX_HEADERi GCC_PREFIX_HEADERitp

Ponadto, choć nie jest to dobrze udokumentowane… Możesz tworzyć module.mapwłasne ramy dla własnych ram i włączać je w ten sam wygodny sposób. Możesz zapoznać się z moim repozytorium github z modułów ObjC-Clang-Modules, aby zobaczyć przykłady implementacji takich cudów.


4

Jeśli znasz C ++ i makra, to

#import "Class.h" 

jest podobne do

{
#pragma once

#include "class.h"
}

co oznacza, że ​​Twoja klasa zostanie załadowana tylko raz po uruchomieniu aplikacji.


Czy jest to obsługiwane użycie #pragma raz? Zawsze myślałem pragmy potrzebne do być wewnątrz tej includ ed pliku do pracy.
uliwitness,

@uliwitness Masz rację. #pragma oncejest umieszczony w dołączonym pliku, a nie w pliku, który wykonuje dołączenie. -1 za to.
herzbube

1

Być może miałem zmienną globalną w jednym z moich .hplików, która była przyczyną problemu, i rozwiązałem ją, dodając externprzed nią.


0

JEŚLI # dołączasz plik dwa razy do plików .h, niż kompilator da błąd. Ale jeśli #importujesz plik więcej niż raz, kompilator go zignoruje.


8
#includeten sam plik dwukrotnie nie powoduje błędu.
kennytm

1
W celu uzupełnienia @ na KennyTM komentarza, # include-ing tego samego pliku dwa razy w tym samym nagłówku nie powoduje błędu kompilacji IF zwykłe GaRDS header (#ifndef FILE_NAME_H #define FILE_NAME_H #end) istnieją. Jest to oczekiwana praktyka. Za pomocą #import osłony nagłówków nie są potrzebne.
jbat100,

@ jbat100: #includeto po prostu mechanizm kopiuj i wklej. Celowe użycie #includewięcej niż raz bez uwzględnienia strażników, np. „Makra X”.
kennytm

Dwukrotne dołączenie pliku może powodować błędy w zależności od tego, co dołączasz. Widziałem kod C, który służył #includedo implementacji pewnego rodzaju szablonów. Zrobili a #define, włączyli nagłówek, #undefd i redidowali #define, po raz drugi zawarli ten sam nagłówek. Spowodowało to, że kod został sparametryzowany, poprawny i uwzględniony dwukrotnie, ponieważ wartość definicji była inna. Są więc zalety używania #include, ale jeśli używasz nowoczesnego języka, takiego jak C ++ lub ObjC, na ogół nie potrzebujesz tego.
uliwitness,

0

#includekiedyś przenosił „rzeczy” z innego pliku do tego, w którym #includejest używany. Np .:

w pliku: main.cpp

#include "otherfile.h"

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

Zabezpieczenie nagłówka jest używane na górze każdego pliku nagłówka (* .h), aby zapobiec dołączeniu tego samego pliku więcej niż jeden raz (jeśli tak się stanie, wystąpią błędy kompilacji).

w pliku: otherfile.h

#ifndef OTHERFILE
#define OTHERFILE

// declare functions, classes or objects here

#endif

nawet jeśli wpiszesz #include„otherfile.h” n w swoim kodzie, to w nim nie zostanie ponownie zapisane.


Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.