Sự khác biệt giữa #import và #include trong Objective-C là gì?


385

Sự khác biệt giữa #import và #include trong Objective-C và có những lúc bạn nên sử dụng cái này hơn cái kia không? Là một người không tán thành?

Tôi đã đọc hướng dẫn sau: http://www.otierney.net/objective-c.html#preamble và đoạn của nó về #import và #include dường như mâu thuẫn với chính nó hoặc ít nhất là không rõ ràng.

Câu trả lời:


340

Lệnh #import đã được thêm vào Objective-C dưới dạng phiên bản cải tiến của #include. Dù điều đó có được cải thiện hay không, tuy nhiên, vẫn là vấn đề tranh luận. #import đảm bảo rằng một tệp chỉ được bao gồm một lần để bạn không bao giờ gặp sự cố với đệ quy bao gồm. Tuy nhiên, hầu hết các tệp tiêu đề phong nha đều tự bảo vệ mình trước điều này, vì vậy nó không thực sự mang lại nhiều lợi ích.

Về cơ bản, tùy bạn quyết định bạn muốn sử dụng cái nào. Tôi có xu hướng #import tiêu đề cho những thứ Objective-C (như định nghĩa lớp và như vậy) và #incolee thứ C tiêu chuẩn mà tôi cần. Ví dụ: một trong các tệp nguồn của tôi có thể trông như thế này:

#import <Foundation/Foundation.h>

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

65
Ngay cả khi các tệp tiêu đề có chứa các bộ bảo vệ, vẫn có một cú đánh hiệu năng trong quá trình biên dịch nếu bạn sử dụng #include - trình biên dịch phải mở từng tệp tiêu đề để thông báo các bộ bảo vệ bao gồm.
Matt Dillard

4
bảo vệ tiêu đề là một chỉ thị tiền xử lý để đảm bảo tiêu đề chỉ được đưa vào một lần trong tệp nguồn.
Jason Coco

8
Tôi nghĩ rằng #import thực sự là một bổ sung của GCC, không phải bởi Objective-C. Bạn có thể sử dụng nó trong các ngôn ngữ không phải ObjC miễn là bạn biên dịch với GCC (hoặc Clang)
Dave DeLong

34
@dave - #import là một bổ sung Objective-C cho bộ tiền xử lý. GCC chỉ hỗ trợ nó trong các tệp nguồn C và C ++, mặc dù họ chính thức đề nghị không sử dụng nó trong C hoặc C ++ để ủng hộ các bộ bảo vệ tiêu đề truyền thống, di động. Tuy nhiên, tất cả các bộ tiền xử lý Objective-C phải bao gồm #import.
Jason Coco

13
Trình bảo vệ tiêu đề là nơi bạn thêm vào đầu: #ifndef myheader #define myheader ... tiếp theo là mã tiêu đề ...#endif
Tim

359

Dường như có rất nhiều nhầm lẫn liên quan đến bộ tiền xử lý.

Trình biên dịch sẽ làm gì khi thấy #includenó thay thế dòng đó bằng nội dung của các tệp được bao gồm, không có câu hỏi nào được hỏi.

Vì vậy, nếu bạn có một tập tin a.hvới nội dung này:

typedef int my_number;

và một tệp b.ccó nội dung này:

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

tập tin b.csẽ được dịch bởi bộ tiền xử lý trước khi biên dịch thành

typedef int my_number;
typedef int my_number;

Điều này sẽ dẫn đến một lỗi biên dịch, vì loại my_numberđược xác định hai lần. Mặc dù định nghĩa là giống nhau nhưng điều này không được phép bởi ngôn ngữ C.

Vì một tiêu đề thường được sử dụng ở nhiều nơi, bao gồm các vệ sĩ thường được sử dụng ở C. Điều này trông giống như sau:

 #ifndef _a_h_included_
 #define _a_h_included_

 typedef int my_number;

 #endif

Tệp b.cvẫn có toàn bộ nội dung của tiêu đề trong đó hai lần sau khi được xử lý trước. Nhưng trường hợp thứ hai sẽ bị bỏ qua vì macro _a_h_included_đã được xác định.

Điều này hoạt động thực sự tốt, nhưng có hai nhược điểm. Trước hết, các vệ sĩ bao gồm phải được viết và tên macro phải khác nhau trong mỗi tiêu đề. Và thứ hai, trình biên dịch vẫn phải tìm tệp tiêu đề và đọc nó thường xuyên như nó được bao gồm.

Objective-C có #importhướng dẫn tiền xử lý (nó cũng có thể được sử dụng cho mã C và C ++ với một số trình biên dịch và tùy chọn). Điều này gần giống như #include, nhưng nó cũng lưu ý bên trong tập tin nào đã được đưa vào. Các #importdòng được chỉ thay thế bằng nội dung của file có tên lần đầu tiên nó gặp phải. Mỗi lần sau đó nó chỉ bị bỏ qua.


5
Đây là câu trả lời tốt hơn so với chấp nhận. @ Guill, bạn nên thay đổi câu trả lời được chấp nhận.
Nguyễn Minh Bình

6
Sau khi thay đổi 4 #includegiây thành #imports trên tệp tiêu đề mẫu 7000 dòng, có một sự cải thiện hiệu suất đáng chú ý trong quá trình biên dịch và khả năng phản hồi nhanh của XCode. (Tôi không nghĩ là mình đang tưởng tượng nó)
bobobobo

63

Tôi đồng ý với Jason.

Tôi bị bắt gặp làm điều này:

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

Đối với GNU gcc, nó tiếp tục phàn nàn rằng hàm time () không được xác định.

Vì vậy, sau đó tôi đã thay đổi #import thành #include và tất cả đều ổn.

Lý do:

Bạn #import <sys / time.h>:
    <sys / time.h> chỉ bao gồm một phần của <time.h> bằng cách sử dụng #defines

Bạn #import <time.h>:
    Không đi. Mặc dù chỉ một phần của <time.h> đã được bao gồm, nhưng theo
    như #import có liên quan, thì tệp đó hiện đã được bao gồm hoàn toàn .

Dòng dưới cùng:

Theo truyền thống, các tiêu đề C / C ++ bao gồm các phần của các tệp khác bao gồm.
Vì vậy, đối với các tiêu đề C / C ++, hãy sử dụng #include.
Đối với các tiêu đề objc / objc ++, hãy sử dụng #import.


2
Có vẻ như clang không có vấn đề không xác định này.
ooops

23

#includehoạt động giống như C #include.

#importtheo dõi những tiêu đề nào đã được đưa vào và bị bỏ qua nếu một tiêu đề được nhập nhiều lần trong một đơn vị biên dịch. Điều này làm cho nó không cần thiết để sử dụng bảo vệ tiêu đề.

Điểm mấu chốt chỉ là sử dụng #importtrong Objective-C và đừng lo lắng nếu tiêu đề của bạn kết thúc việc nhập một cái gì đó nhiều lần.


2
giả vờ trong một phút mà tôi không quen thuộc với C #include (chủ yếu là vì tôi không phải), sự khác biệt chính giữa #include và #import là gì? Ngoài ra, bạn có thể cho tôi biết bảo vệ tiêu đề là gì?
Ryan Guill

@Ryan: Nhìn vào câu trả lời của Sven.
Adrian Petrescu

13

Tôi biết chủ đề này đã cũ ... nhưng trong "thời hiện đại" .. có một "chiến lược bao gồm" vượt trội hơn rất nhiều thông qua các mô-đun của clang@import - đó là bị bỏ qua ..

Các mô-đun cải thiện quyền truy cập vào API của các thư viện phần mềm bằng cách thay thế mô hình đưa vào tiền xử lý văn bản bằng mô hình ngữ nghĩa mạnh mẽ hơn, hiệu quả hơn. Từ quan điểm của người dùng, mã chỉ trông hơi khác nhau, bởi vì người ta sử dụng khai báo nhập thay vì chỉ thị tiền xử lý #include:

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

hoặc là

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

Tuy nhiên, quá trình nhập mô-đun này hoạt động hoàn toàn khác với #include tương ứng: khi trình biên dịch nhìn thấy mô-đun nhập ở trên, nó sẽ tải một biểu diễn nhị phân của mô-đun và cung cấp API trực tiếp cho ứng dụng. Các định nghĩa tiền xử lý đi trước khai báo nhập khẩu không có tác động đến API được cung cấp ... vì bản thân mô-đun được biên dịch thành một mô-đun độc lập, riêng biệt. Ngoài ra, bất kỳ cờ liên kết nào được yêu cầu để sử dụng mô-đun sẽ tự động được cung cấp khi mô-đun được nhập. Mô hình nhập khẩu ngữ nghĩa này giải quyết nhiều vấn đề của mô hình đưa vào tiền xử lý.

Để kích hoạt các mô-đun, hãy chuyển cờ dòng lệnh -fmodulesaka CLANG_ENABLE_MODULEStrong Xcode- tại thời gian biên dịch. Như đã đề cập ở trên .. chiến lược này làm giảm BẤT K and và TẤT CẢ LDFLAGS. Như trong, bạn có thể XÓA bất kỳ cài đặt "Other_LDFLAGS" nào, cũng như bất kỳ giai đoạn "Liên kết" nào ..

nhập mô tả hình ảnh ở đây

Tôi thấy thời gian biên dịch / khởi chạy để "cảm thấy" nhiều snappier hơn (hoặc có thể, chỉ có độ trễ trong khi "liên kết"?) .. và cũng, cung cấp một cơ hội tuyệt vời để lọc tệp Project-Prefix.pch hiện tại và tương ứng xây dựng các thiết lập, GCC_INCREASE_PRECOMPILED_HEADER_SHARING, GCC_PRECOMPILE_PREFIX_HEADER, và GCC_PREFIX_HEADERvv

Ngoài ra, trong khi không có tài liệu tốt Bạn có thể tạo module.maps cho các khung riêng của mình và đưa chúng vào cùng một cách thuận tiện. Bạn có thể xem repo github ObjC-Clang-Modules của tôi để biết một số ví dụ về cách thực hiện các phép lạ như vậy.


4

Nếu bạn đã quen thuộc với C ++ và macro, thì

#import "Class.h" 

tương tự như

{
#pragma once

#include "class.h"
}

có nghĩa là Lớp của bạn sẽ chỉ được tải một lần khi ứng dụng của bạn chạy.


Đây có phải là hỗ trợ sử dụng #pragma một lần không? Tôi luôn luôn nghĩ rằng pragma cần thiết để được bên trong các includ ed tập tin để làm việc.
uliwitness

@uliwitness Bạn đúng rồi. #pragma onceđược đặt trong tệp bao gồm, không phải tệp thực hiện bao gồm. -1 cho điều đó.
herzbube

1

Trong trường hợp có thể tôi đã có một biến toàn cục trong một trong các .htệp gây ra sự cố và tôi đã giải quyết nó bằng cách thêm externvào trước nó.


0

NẾU bạn # loại trừ một tệp hai lần trong các tệp .h hơn trình biên dịch sẽ báo lỗi. Nhưng nếu bạn #import một tệp nhiều lần trình biên dịch sẽ bỏ qua nó.


8
#includecùng một tệp hai lần không dẫn đến một lỗi.
kennytm

1
Để bổ sung cho nhận xét của @ KennyTM, # bao gồm cùng một tệp hai lần trong cùng một tiêu đề không dẫn đến lỗi biên dịch NẾU các tiêu đề thông thường (#ifndef FILE_NAME_H #define FILE_NAME_H #end) đều ở đó. Đây là thực tế dự kiến. Sử dụng #import bộ bảo vệ tiêu đề không cần thiết.
jbat100

@ jbat100: #includechỉ đơn giản là một cơ chế sao chép và dán. Có chủ ý sử dụng #includenhiều lần mà không bao gồm các vệ sĩ, ví dụ "macro X".
kennytm

Bao gồm một tệp hai lần có thể dẫn đến lỗi tùy thuộc vào những gì bạn bao gồm. Tôi đã thấy mã C được sử dụng #includeđể triển khai một loại mẫu. Họ đã làm một #define, bao gồm một tiêu đề, #undefd và làm lại #define, bao gồm cùng một tiêu đề lần thứ hai. Điều này dẫn đến mã được tham số hóa, hợp lệ và được bao gồm hai lần, vì giá trị của định nghĩa là khác nhau. Vì vậy, có những lợi thế khi sử dụng #include, nhưng nếu bạn đang sử dụng một ngôn ngữ hiện đại như C ++ hoặc ObjC, bạn thường không cần điều này.
uliwitness

0

#includenó được sử dụng để lấy "những thứ" từ một tập tin khác đến tập tin #includeđược sử dụng. Ex:

trong tập tin: main.cpp

#include "otherfile.h"

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

Bảo vệ tiêu đề được sử dụng ở đầu mỗi tệp tiêu đề (* .h) để ngăn chặn bao gồm cùng một tệp sau đó một lần (nếu điều đó xảy ra, bạn sẽ gặp lỗi biên dịch).

trong tập tin: otherfile.h

#ifndef OTHERFILE
#define OTHERFILE

// declare functions, classes or objects here

#endif

ngay cả khi bạn đặt #include thời gian "otherfile.h" trong mã của mình, điều này bên trong nó sẽ không được khai báo lại.


Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.