[譯] 為什么 #import 順序?qū)σ蕾嚬芾砗苤匾?/h2>

在 Objective-C 中,圍繞 #import 順序存在一些微妙的問題。你可能不相信我,直到你嘗試在新項目中重復(fù)使用舊代碼。

狂野的 #import! 一文中,我們探討了 #import 指令過多帶來的問題。但導(dǎo)入的太少也有可能導(dǎo)致頭文件不好,特別是如果你沒有注意 .m 文件中的 #import 順序。

使導(dǎo)入最少化和完整化

在導(dǎo)入時,頭文件應(yīng)滿足這兩個條件:

  • 應(yīng)盡量少
  • 應(yīng)盡量完整

"最少 "僅表示頭文件導(dǎo)入的內(nèi)容不應(yīng)超過其需要。
"完整 "是指頭文件導(dǎo)入編譯所需的所有內(nèi)容??紤]一下:

#import "foo.h"
#import "bar.h"

如果刪除 foo.h(或改變順序)導(dǎo)致 bar.h 無法編譯,那么 bar.h 并不完整。

發(fā)現(xiàn)不完整的 Header

依賴預(yù)編譯頭文件是導(dǎo)致頭文件不完整的一種情況。特別是,預(yù)編譯的頭文件包含某個特定的頭文件,并不意味著你可以在其他地方省略它。

另一種頭文件不完整的情況是 #import 順序不當(dāng),掩蓋了依賴關(guān)系。在基于 C 的語言中,程序員在開始編寫實現(xiàn)文件時,通常會在最大范圍內(nèi)包含最通用的頭文件。然后依次向下,直到包含最具體的頭文件:

  • 1、系統(tǒng)頭文件
  • 2、其他頭文件
  • 3、最后,該文件自身的頭文件

這是一種倒退。考慮一下依賴于 <QuartzCore/QuartzCore.h> 的頭文件 foo.h。如果 foo.m 首先導(dǎo)入 QuartzCore,然后導(dǎo)入其他內(nèi)容,最后才導(dǎo)入自己的頭文件,那么你可能就不會覺得有必要在 foo.h 中導(dǎo)入 QuartzCore 了。......而下一個程序員如果直接導(dǎo)入 foo.h,就會導(dǎo)致程序崩潰。

原作者在評論答疑中對于此處的解釋:
#import is a preprocessor directive. It’s effectively the same as copying and pasting. So if you import QuartzCore first, and your “self” header last, it all expands in your .m before it’s compiled. That’s why the ordering matters.

But if it’s expanded before other headers that use it, those headers will pick it up by accident, not by design. By importing it at the end, any headers that need it but don’t import it themselves will cause a compile-time error. Which is what I want. I want the compiler to tell me about headers that don’t declare their dependencies.

Hopefully this will gradually become history with modules and @import.

#import是一個預(yù)處理器指令。它實際上與復(fù)制和粘貼相同。因此,如果你先導(dǎo)入 QuartzCore,最后才導(dǎo)入自己頭文件,那么在編譯之前,所有文件都會在 .m 中展開。這就是為什么順序很重要。

但是,如果在使用它的其他頭文件之前展開它,這些頭文件就會意外而非有意地使用它。如果在末尾導(dǎo)入,任何需要它但自己沒有導(dǎo)入的頭文件都會導(dǎo)致編譯時出錯。這正是我想要的。我希望編譯器能告訴我那些沒有聲明其依賴關(guān)系的頭文件。

希望隨著模塊(modules)和 @import 的使用,這個問題會逐漸成為歷史。

好的 #import 順序

信息披露:以下書籍鏈接為聯(lián)盟鏈接。如果您購買任何商品,我將賺取傭金,您無需支付額外費用。

解決辦法很簡單:顛倒順序!從最具體的開始,然后再到最一般的。最重要的是,先包含你自己的頭文件。約翰-拉科斯(John Lakos)所著的《大型 C++ 軟件設(shè)計》是我所知道的唯一一本關(guān)于 "物理設(shè)計"——如何將源代碼編排到文件中的書。

Large-Scale C++ Software Design

該書作者指出:

Latent usage errors can be avoided by ensuring that the .h file of a component parses by itself—without externally-provided declarations or definitions… Including the .h file as the very first line of the .c file ensures that no critical piece of information intrinsic to the physical interface of the component is missing from the .h file (or, if there is, that you will find out about it as soon as you try to compile the .c file).
將 .h 文件作為 .c 文件的第一行,可以確保 .h 文件中不會缺少組件物理接口的關(guān)鍵信息(如果缺少,也不會在編譯 .c 文件時發(fā)現(xiàn))。

我是這么做的。如果我在編寫 foo.m,我會首先導(dǎo)入 foo.h,并用空行將其與其他導(dǎo)入內(nèi)容隔開。然后再按順序?qū)肫渌袑?dǎo)入文件:

#import "foo.h"
#import "abc.h"
#import "def.h"
#import <Abc/Abc.h>

排序可以幫助我找到重復(fù)的內(nèi)容。它還會把角括號導(dǎo)入 <> 放在引號導(dǎo)入之后,這樣最一般的標(biāo)題就會放在最后。

譯自:Why #import Order Matters for Dependency Management
侵刪

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容