ความแตกต่างระหว่าง #import และ #include ใน Objective-C คืออะไร


384

อะไรคือความแตกต่างระหว่าง #import และ #include ใน Objective-C และมีเวลาที่คุณควรใช้อันอื่น เลิกใช้แล้วหรือไม่

ฉันกำลังอ่านบทช่วยสอนต่อไปนี้: http://www.otierney.net/objective-c.html#preambleและย่อหน้าเกี่ยวกับ #import และ #include ดูเหมือนจะขัดแย้งกับตัวเองหรืออย่างน้อยก็ไม่มีความชัดเจน

คำตอบ:


341

#import directive ถูกเพิ่มไปยัง Objective-C เป็นเวอร์ชันที่ปรับปรุงของ #include อย่างไรก็ตามการปรับปรุงหรือไม่นั้นยังคงเป็นประเด็นถกเถียง #import ทำให้มั่นใจได้ว่าไฟล์จะถูกรวมเพียงครั้งเดียวเพื่อให้คุณไม่เคยมีปัญหากับการเรียกซ้ำรวม อย่างไรก็ตามไฟล์ส่วนหัวที่เหมาะสมส่วนใหญ่จะป้องกันตนเองจากสิ่งนี้ดังนั้นจึงไม่ได้ประโยชน์มากนัก

โดยทั่วไปขึ้นอยู่กับคุณที่จะตัดสินใจว่าคุณต้องการใช้อะไร ฉันมักจะ #import headers สำหรับ Objective-C things (เช่นคำจำกัดความของคลาสและเช่นนั้น) และ #include stuff C มาตรฐานที่ฉันต้องการ ตัวอย่างเช่นหนึ่งในไฟล์ต้นฉบับของฉันอาจมีลักษณะเช่นนี้:

#import <Foundation/Foundation.h>

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

63
แม้ว่าไฟล์ส่วนหัวจะมียามรวมอยู่ แต่ก็ยังคงมีประสิทธิภาพการทำงานในระหว่างการรวบรวมหากคุณใช้ #include คอมไพเลอร์จะต้องเปิดแต่ละไฟล์ส่วนหัวเพื่อแจ้งให้ทราบถึงการรวมการ์ด
Matt Dillard

4
ตัวป้องกันส่วนหัวเป็นคำสั่งของตัวประมวลผลล่วงหน้าที่รับรองว่าส่วนหัวจะรวมเพียงครั้งเดียวในไฟล์ต้นฉบับ
เจสันโคโค่

8
ฉันคิดว่า #import เป็นส่วนเพิ่มเติมของ GCC ไม่ใช่โดย Objective-C คุณสามารถใช้มันในภาษาที่ไม่ใช่ ObjC ได้ตราบใดที่คุณคอมไพล์กับ GCC (หรือเสียงดังกราว)
เดฟเดอลอง

33
@dave - #import เป็นการเพิ่ม Objective-C ให้กับตัวประมวลผลล่วงหน้า GCC สนับสนุนไฟล์ดังกล่าวในไฟล์ต้นฉบับ C และ C ++ ด้วยเช่นกันถึงแม้ว่าพวกเขาแนะนำอย่างเป็นทางการว่าไม่ควรใช้ไฟล์นี้ใน C หรือ C ++ เพื่อปกป้องการ์ดส่วนหัวแบบดั้งเดิมแบบพกพา ตัวประมวลผลล่วงหน้า Objective-C ทั้งหมดต้องมี #import อย่างไรก็ตาม
เจสันโคโค่

13
ยามหัวเป็นที่ที่คุณเพิ่มไปด้านบน: #ifndef myheader #define myheader ... ตามด้วยรหัสหัว ...#endif
ทิม

358

ดูเหมือนจะมีความสับสนมากมายเกี่ยวกับพรีโปรเซสเซอร์

สิ่งที่คอมไพเลอร์ทำเมื่อเห็น#includeว่ามันแทนที่บรรทัดนั้นด้วยเนื้อหาของไฟล์ที่รวมไว้ไม่มีการถามคำถาม

ดังนั้นหากคุณมีไฟล์ที่a.hมีเนื้อหานี้:

typedef int my_number;

และไฟล์ที่b.cมีเนื้อหานี้:

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

ไฟล์b.cจะถูกแปลโดยตัวประมวลผลก่อนก่อนที่จะรวบรวม

typedef int my_number;
typedef int my_number;

ซึ่งจะส่งผลให้เกิดข้อผิดพลาดในการรวบรวมเนื่องจากประเภทที่my_numberกำหนดไว้สองครั้ง แม้ว่าคำจำกัดความจะเหมือนกัน แต่ไม่อนุญาตให้ใช้ภาษา C

เนื่องจากส่วนหัวมักจะใช้ในมากกว่าหนึ่งสถานที่รวมถึงยามมักจะใช้ใน C. นี้มีลักษณะเช่นนี้:

 #ifndef _a_h_included_
 #define _a_h_included_

 typedef int my_number;

 #endif

ไฟล์b.cจะยังคงมีเนื้อหาทั้งหมดของส่วนหัวในนั้นสองครั้งหลังจากถูกประมวลผลล่วงหน้า แต่อินสแตนซ์ที่สองจะถูกละเว้นเนื่องจากแมโคร_a_h_included_จะถูกกำหนดไว้แล้ว

วิธีนี้ใช้งานได้ดี แต่มีข้อเสียสองประการ ก่อนอื่นต้องเขียนยามรวมและชื่อแมโครจะต้องแตกต่างกันในทุกส่วนหัว และประการที่สองคอมไพเลอร์ยังคงมองหาไฟล์ส่วนหัวและอ่านได้บ่อยเท่าที่มันรวมอยู่

Objective-C มี#importคำสั่ง preprocessor (สามารถใช้กับรหัส C และ C ++ พร้อมคอมไพเลอร์และตัวเลือกบางตัว) สิ่งนี้เกือบจะเหมือนกัน#includeแต่ยังบันทึกภายในไฟล์ที่รวมอยู่แล้ว #importสายจะถูกแทนที่โดยเฉพาะเนื้อหาของไฟล์ที่มีชื่อเป็นครั้งแรกที่จะพบ ทุกครั้งหลังจากนั้นมันก็จะถูกละเว้น


4
นี่คือคำตอบที่ดีกว่าคำตอบที่ยอมรับ @Gill คุณควรเปลี่ยนคำตอบที่ยอมรับ
เหงียนมินห์บินห์

5
หลังจากเปลี่ยน 4 #includes #importเป็นไฟล์เทมเพลตส่วนหัวของบรรทัด 7000 มีการปรับปรุงประสิทธิภาพที่เห็นได้ชัดเจนในการรวบรวมและการตอบสนอง Intellisense XCode (ฉันไม่คิดว่าฉันจะจินตนาการ)
bobobobo

62

ฉันเห็นด้วยกับเจสัน

ฉันถูกจับได้ว่าทำสิ่งนี้:

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

สำหรับ GNU gcc มันยังคงบ่นว่า time () ฟังก์ชั่นไม่ได้กำหนดไว้

ดังนั้นฉันจึงเปลี่ยน #import เป็น #include และทุกอย่างก็โอเค

เหตุผล:

คุณ #import <sys / time.h>:
    <sys / time.h> รวมเฉพาะบางส่วนเท่านั้นของ <time.h> โดยใช้ #defines

คุณ #import <time.h>:
    ไม่ไป แม้ว่าจะมีเพียงส่วนหนึ่งของ <time.h> ที่ได้ถูกรวมไว้แล้ว
    เท่าที่ #import เกี่ยวข้อง แต่ขณะนี้ไฟล์ดังกล่าวนั้นได้ดำเนินการเรียบร้อยแล้วรวมไว้

บรรทัดล่าง:

ส่วนหัว C / C ++ รวมถึงส่วนต่าง ๆของไฟล์ include
ดังนั้นสำหรับส่วนหัว C / C ++ ให้ใช้ #include
สำหรับส่วนหัว objc / objc ++ ให้ใช้ #import


1
ดูเหมือนว่าเสียงดังกราวไม่ได้มีปัญหาที่ไม่ได้กำหนดนี้
ooops

23

#include ทำงานเหมือน C #includeทำงานเช่นเดียวกับซี

#importติดตามว่าส่วนหัวใดรวมอยู่แล้วและจะถูกข้ามหากส่วนหัวถูกนำเข้ามากกว่าหนึ่งครั้งในหน่วยการรวบรวม สิ่งนี้ทำให้ไม่จำเป็นต้องใช้ยามหัวกระดาษ

บรรทัดล่างใช้เพียง#importใน Objective-C และไม่ต้องกังวลหากส่วนหัวของคุณเลิกการนำเข้าบางอย่างมากกว่าหนึ่งครั้ง


2
ทำท่านาทีที่ฉันไม่คุ้นเคยกับ C #include (ส่วนใหญ่เป็นเพราะฉันไม่ได้) อะไรคือความแตกต่างที่สำคัญระหว่าง #include และ #import นอกจากนี้คุณสามารถบอกฉันว่ายามหัวคืออะไร?
Ryan Guill

@Ryan: ดูคำตอบของ Sven
Adrian Petrescu

13

ฉันรู้ว่าหัวข้อนี้เก่า ... แต่ใน "ยุคปัจจุบัน" .. มีกลยุทธ์ที่ดีกว่า "รวมถึง" ผ่านโมดูลของเสียงดังกราว@import - ที่มองข้าม ..

โมดูลปรับปรุงการเข้าถึง API ของไลบรารีซอฟต์แวร์โดยแทนที่โมเดลการรวมตัวประมวลผลก่อนข้อความด้วยโมเดล semantic ที่มีประสิทธิภาพและมีประสิทธิภาพมากขึ้น จากมุมมองของผู้ใช้รหัสมีลักษณะแตกต่างกันเพียงเล็กน้อยเท่านั้นเพราะใช้การประกาศการนำเข้ามากกว่า #include คำสั่งประมวลผลล่วงหน้า:

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

หรือ

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

อย่างไรก็ตามการนำเข้าโมดูลนี้ทำงานค่อนข้างแตกต่างจาก #include ที่สอดคล้องกัน: เมื่อคอมไพเลอร์เห็นการอิมพอร์ตโมดูลด้านบนมันจะโหลดการแทนไบนารีของโมดูลและทำให้ API พร้อมใช้งานกับแอปพลิเคชันโดยตรง คำจำกัดความของตัวประมวลผลล่วงหน้าที่นำหน้าการประกาศการนำเข้าไม่มีผลกระทบต่อ API ที่ระบุ ... เนื่องจากโมดูลตัวเองได้รับการรวบรวมเป็นโมดูลแยกต่างหากแบบสแตนด์อโลน นอกจากนี้แฟล็กลิงเกอร์ใด ๆ ที่จำเป็นต้องใช้โมดูลจะได้รับโดยอัตโนมัติเมื่อนำเข้าโมดูล โมเดลการอิมพอร์ตแบบ semantic นี้ระบุปัญหาหลายอย่างของโมเดลการรวมตัวประมวลผลล่วงหน้า

หากต้องการเปิดใช้งานโมดูลให้ส่งผ่านการตั้งค่าสถานะบรรทัดคำสั่ง-fmodulesaka CLANG_ENABLE_MODULESในXcode- ณ เวลารวบรวม ดังกล่าวข้างต้น .. กลยุทธ์นี้ obviates ใด ๆ LDFLAGSและทั้งหมด เช่นเดียวกับคุณสามารถลบการตั้งค่า "OTHER_LDFLAGS" ใด ๆ เช่นเดียวกับขั้นตอน "การเชื่อมโยง"

ป้อนคำอธิบายรูปภาพที่นี่

ฉันพบการรวบรวม / เปิดตัวครั้งเพื่อ "รู้สึก" snappier มาก (หรืออาจจะมีความล่าช้าน้อยกว่าในขณะที่ "เชื่อมโยง"?) .. และยังให้โอกาสที่ดีในการกำจัดไฟล์ Project-Prefix.pch ภายนอกและตอนนี้ สอดคล้องกับการตั้งค่าการสร้างGCC_INCREASE_PRECOMPILED_HEADER_SHARING, GCC_PRECOMPILE_PREFIX_HEADERและGCC_PREFIX_HEADERอื่น ๆ

นอกจากนี้ในขณะที่ไม่มีเอกสารที่ดี ... คุณสามารถสร้างmodule.maps สำหรับกรอบงานของคุณเองและรวมไว้ในแบบเดียวกัน คุณสามารถดู gitub ObjC-Clang-Modules ของฉันสำหรับตัวอย่างบางส่วนของวิธีการใช้ปาฏิหาริย์ดังกล่าว


4

หากคุณคุ้นเคยกับ C ++ และมาโคร

#import "Class.h" 

เหมือนกับ

{
#pragma once

#include "class.h"
}

ซึ่งหมายความว่าคลาสของคุณจะถูกโหลดเพียงครั้งเดียวเมื่อแอปของคุณทำงาน


นี่เป็นการใช้งาน #pragma ที่ได้รับการสนับสนุนครั้งเดียวหรือไม่ ฉันคิดเสมอว่า pragma จำเป็นต้องอยู่ในไฟล์includ edเพื่อทำงาน
uliwitness

@uliwitness คุณถูกต้อง #pragma onceถูกวางในไฟล์ที่รวมไว้ไม่ใช่ไฟล์ที่ทำการรวม -1 สำหรับสิ่งนั้น
herzbube

1

ในกรณีที่ฉันมีตัวแปรทั่วโลกในหนึ่งใน.hไฟล์ของฉันที่ทำให้เกิดปัญหาและฉันแก้ไขได้โดยการเพิ่มexternในด้านหน้าของมัน


0

หากคุณรวม # ไฟล์สองครั้งในไฟล์. h ที่คอมไพเลอร์จะให้ข้อผิดพลาด แต่ถ้าคุณ #import ไฟล์มากกว่าหนึ่งครั้งคอมไพเลอร์จะไม่สนใจมัน


8
#includeไฟล์เดียวกันสองครั้งไม่ส่งผลให้เกิดข้อผิดพลาด
kennytm

1
ความคิดเห็นของส่วนประกอบ @ KennyTM ของ # รวมไอเอ็นจีไฟล์เดียวกันสองครั้งในหัวเดียวกันไม่ได้ผลในรวบรวมข้อผิดพลาดถ้า Gards หัวปกติ (คำสั่ง #ifdef FILE_NAME_H #define FILE_NAME_H #END) จะมี นี่คือการปฏิบัติที่คาดหวัง การใช้ #import ตัวป้องกันส่วนหัวไม่จำเป็น
jbat100

@ jbat100: #includeเป็นเพียงกลไกการคัดลอกและวาง มีการใช้งาน#includeมากกว่าหนึ่งครั้งอย่างรอบคอบโดยไม่ต้องมีเจ้าหน้าที่รักษาความปลอดภัยเช่น "มาโคร X"
kennytm

การรวมไฟล์สองครั้งอาจส่งผลให้เกิดข้อผิดพลาดขึ้นอยู่กับสิ่งที่คุณรวม ฉันเห็นรหัส C ที่ใช้#includeในการปรับใช้เทมเพลตชนิดหนึ่ง พวกเขาทำ#define, รวมส่วนหัว, #undefd และ redid the #define, รวมส่วนหัวเดียวกันเป็นครั้งที่สอง สิ่งนี้ส่งผลให้โค้ดถูกทำให้เป็นพารามิเตอร์ใช้งานได้และรวมสองครั้งเนื่องจากค่าของการกำหนดแตกต่างกัน มีข้อดีที่จะใช้#includeแต่ถ้าคุณกำลังใช้ภาษาสมัยใหม่เช่น C ++ หรือ ObjC โดยทั่วไปคุณไม่จำเป็นต้องใช้สิ่งนี้
uliwitness

0

#includeมันเคยได้รับ "สิ่งของ" จากไฟล์อื่นไปยังไฟล์ที่#includeใช้แล้ว Ex:

ในไฟล์: main.cpp

#include "otherfile.h"

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

Header Guard ใช้ที่ด้านบนของแต่ละไฟล์ส่วนหัว (* .h) เพื่อป้องกันการรวมไฟล์เดียวกันมากกว่าหนึ่งครั้ง (ถ้าเกิดขึ้นคุณจะได้รับข้อผิดพลาดในการคอมไพล์)

ในไฟล์: otherfile.h

#ifndef OTHERFILE
#define OTHERFILE

// declare functions, classes or objects here

#endif

แม้ว่าคุณจะใส่#include"otherfile.h" n เวลาในรหัสของคุณสิ่งนี้อยู่ภายในมันจะไม่ถูกประกาศใหม่


0
#include + guard == #import

#include guardWiki - แมโครการ์ดตัวป้องกันส่วนหัวหรือตัวป้องกันไฟล์เพื่อป้องกันการรวมส่วนหัวโดยpreprocessorที่สามารถชะลอเวลาสร้าง

ขั้นตอนต่อไปคือ

.pch[เกี่ยวกับ] =>@import [เกี่ยวกับ]

[#import in .hหรือ.m]

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.