【问题标题】:@import vs #import - iOS 7@import 与 #import - iOS 7
【发布时间】:2013-09-27 15:45:39
【问题描述】:

我正在使用一些新的 iOS 7 功能,并使用 WWDC 视频“在 iOS 上实现 Engaging UI”中讨论的一些图像效果。为了在会话的源代码中产生模糊效果,UIImage 通过导入 UIKit 的类别进行了扩展,如下所示:

@import UIKit;

我想我在另一个会话视频中看到了一些关于此的内容,但我找不到它。我正在寻找有关何时使用它的任何背景信息。它只能与 Apple 框架一起使用吗?使用此编译器指令的好处是否足以让我返回并更新旧代码?

【问题讨论】:

标签: ios objective-c import ios7


【解决方案1】:

这是一个名为 Modules 或“语义导入”的新功能。会话205404WWDC 2013 视频中有更多信息。这是预编译头文件的一种更好的实现。 您可以将模块与 iOS 7 和 Mavericks 中的任何系统框架一起使用。 模块是框架可执行文件及其头文件的封装,被吹捧为比 #import 更安全、更高效。

使用@import 的一大优势是您无需在项目设置中添加框架,它会自动完成。这意味着您可以跳过单击加号按钮并搜索框架(金色工具箱)的步骤,然后将其移至“框架”组。它将使许多开发人员从神秘的“链接器错误”消息中解脱出来。

您实际上不需要使用 @import 关键字。 如果您选择使用模块,所有 #import#include 指令都会自动映射为使用 @import .这意味着您不必更改源代码(或从其他地方下载的库的源代码)。据说使用模块也可以提高构建性能,尤其是在您没有很好地使用 PCH 或者您的项目有很多小源文件的情况下。

模块是为大多数 Apple 框架(UIKit、MapKit、GameKit 等)预先构建的。你可以将它们与你自己创建的框架一起使用:如果你在 Xcode 中创建一个 Swift 框架,它们会自动创建,你可以为any Apple or 3rd-party library 手动创建一个“.modulemap”文件。

您可以使用代码完成来查看可用框架的列表:

在 Xcode 5 的新项目中默认启用模块。要在旧项目中启用它们,请进入项目构建设置,搜索“模块”并将“启用模块”设置为“是”。 “链接框架”也应该是“是”:

您必须使用 Xcode 5 和 iOS 7 或 Mavericks SDK,但您仍然可以为较旧的操作系统(例如 iOS 4.3 或其他)发布。 模块不会改变您的代码的构建方式或任何源代码。


来自 WWDC 幻灯片:

  • 导入框架的完整语义描述
  • 不需要解析头部
  • 导入框架界面的更好方法
  • 加载二进制表示
  • 比预编译头文件更灵活
  • 不受本地宏定义的影响(例如#define readonly 0x01
  • 默认为新项目启用

显式使用模块:

#import <Cocoa/Cocoa.h> 替换为@import Cocoa;

您也可以使用此表示法只导入一个标头:

@import iAd.ADBannerView;

Xcode 中的子模块会自动完成。

【讨论】:

  • @DaveDeLong & Klaas:谢谢!我不得不承认,当我第一次回答这个问题时,我对模块一无所知。我去看了第 404 课来学习它。 Doug Gregor(LLVM 人)的演讲做得非常好。还有一个 C++ Modules talk 解释了这里的优点:youtube.com/watch?v=4Xo9iH5VLQ0
  • @nevan--感谢您的回答。我只是想补充一点,模块目前还不支持 3rd 方和您自己的框架。
  • 你可以用它来上课吗?
  • 如果提供了适当的 module.map,我认为您应该能够@import 3rd 方框架。 LLVM clang 模块文档:clang.llvm.org/docs/Modules.html#module-map-language
  • 哦,实际上看起来@import sqlite3 对我有用,因为我为它创建了自己的module.map,当我意识到sqlite 包含在OS X 中并删除了我的module.map 时,编译器继续使用陈旧的模块。
【解决方案2】:

您可以在《Learning Cocoa with Objective-C》一书中找到很好的答案(ISBN:978-1-491-90139-7)

模块是一种将文件和库包含并链接到项目中的新方法。要了解模块是如何工作的以及它们有什么好处,回顾一下 Objective-C 的历史和#import 语句很重要 每当你想包含一个文件以供使用时,你通常会有一些看起来像这样的代码:

#import "someFile.h"

或者在框架的情况下:

#import <SomeLibrary/SomeFile.h>

由于 Objective-C 是 C 编程语言的超集,#import 语句是对 C 的 #include 语句的一个小改进。 #include 语句非常简单;它会在编译期间将在包含文件中找到的所有内容复制到您的代码中。这有时会导致严重的问题。例如,假设您有两个头文件:SomeFileA.hSomeFileB.hSomeFileA.h 包括SomeFileB.hSomeFileB.h 包括SomeFileA.h。这会创建一个循环,并且会混淆编译器。为了解决这个问题,C 程序员必须编写防止此类事件发生的防护措施。

使用#import 时,您无需担心此问题或编写标头保护来避免它。然而,#import 仍然只是一个美化的复制和粘贴操作,在许多其他较小但仍然非常危险的问题中导致编译时间缓慢(例如包含的文件覆盖了您在自己的代码中其他地方声明的内容。)

模块试图解决这个问题。它们不再是复制并粘贴到源代码中,而是包含文件的序列化表示,只有在需要它们的时间和地点才能导入到源代码中。通过使用模块,代码通常会编译得更快,并且比使用 #include 或 #import 更安全。

回到前面导入框架的例子:

#import <SomeLibrary/SomeFile.h>

要将此库作为模块导入,代码将更改为:

@import SomeLibrary;

这具有 Xcode 将 SomeLibrary 框架自动链接到项目中的额外好处。模块还允许您仅将您真正需要的组件包含到您的项目中。例如,如果您想在 AwesomeLibrary 框架中使用 AwesomeObject 组件,通常您必须导入所有内容才能使用其中的一个。但是,使用模块,您可以只导入您想要使用的特定对象:

@import AwesomeLibrary.AwesomeObject;

对于在 Xcode 5 中创建的所有新项目,默认启用模块。如果您想在旧项目中使用模块(并且您确实应该),则必须在项目的构建设置中启用它们。完成此操作后,您可以在代码中同时使用 #import@import 语句,而无需担心。

【讨论】:

  • 我的项目 (Xcode 6) 中没有我第一次在 Xcode 4 上启动用于启用模块的选项。我可以以某种方式手动添加吗?
  • 构建目标是 iOS 6,我认为这是问题
【解决方案3】:

目前仅适用于内置系统框架。如果您像苹果一样使用#import,仍然会在应用程序委托中导入UIKit 框架,它会被替换(如果模块已打开并且它被识别为系统框架)并且编译器会将其重新映射为模块导入而不是无论如何导入头文件。 因此,保留#import 与在可能的情况下将其转换为模块导入相同

【讨论】:

    【解决方案4】:

    @import Module(ObjC) 或语义导入

    历史:

    #include => #import => Precompiled Headers .pch => @import Module(ObjC); => import Module(Swift)
    

    [#include vs #import]
    [Precompiled Headers .pch]

    [import Module(Swift)]

    它是LLVM Modules的一部分

    @import &lt;module_name&gt;; 声明告诉编译器加载(而不是编译)一个预编译模块的二进制文件,它减少构建时间。以前编译器每次运行时都会编译依赖项,但现在应该预先编译并加载

    //previously
    run into dependency -> compile dependency
    run into dependency -> compile dependency
    
    //@import
    compile dependency 
        run into dependency -> load compiled binary
        run into dependency -> load compiled binary
    

    [Modulemap] - 模块和头文件之间的桥梁

    Xcode

    Enable Modules(C and Objective-C)(CLANG_ENABLE_MODULES) - CLANG #include, #import 指令会自动转换为 @import,从而带来所有优势。 Modulemap 允许无缝执行,因为包含标题和子/模块之间的映射

    通过-fmodules

    #include, #import -> @import
    

    Link Frameworks Automatically(CLANG_MODULES_AUTOLINK) - 启用 system 模块自动链接。需要激活CLANG_ENABLE_MODULES。自动链接允许基于#import, @import(Objective-C), import(Swift) 传递-framework &lt;framework_name&gt;

    如果否 - 传递 -fno-autolink 标志

    如果您想手动处理系统(#import &lt;UIKit/UIKit.h&gt;)链接(而不是自动链接),您有两种变体:

    1. 将依赖添加到General -&gt; Frameworks and Libraries or Frameworks, Libraries, and Embedded Content

    2. Build Settings -&gt; Other Linker Flags(OTHER_LDFLAGS) -&gt; -framework &lt;module_name&gt;

    如果出现以下情况,则引发下一个错误:

    • CLANG_ENABLE_MODULES 已禁用
    • CLANG_MODULES_AUTOLINK 已禁用且无法手动链接
    Undefined symbol: _OBJC_CLASS_$_UIView
    
    Undefined symbols for architecture x86_64:
      "_OBJC_CLASS_$_UIView", referenced from:
          objc-class-ref in ClassB.o
    ld: symbol(s) not found for architecture x86_64
    clang: error: linker command failed with exit code 1
    

    逆向工程

    otool -l <binary> 
    //-l print the load commands
    //find LC_LINKER_OPTION
    //cmd LC_LINKER_OPTION
    

    【讨论】:

      【解决方案5】:

      似乎从 XCode 7.x 开始,在使用 CLANG_ENABLE_MODULES 启用 clang 模块时会出现很多警告

      看看Lots of warnings when building with Xcode 7 with 3rd party libraries

      【讨论】:

      • 是的,我也有这个问题,但是将其设置为 NO 会删除所有警告。这样做会不会有副作用??
      【解决方案6】:

      使用模块有一些好处。除非创建了模块映射,否则您只能将它与 Apple 的框架一起使用。 @import 有点类似于添加到 .pch 文件时的预编译头文件,这是一种调整应用程序编译过程的方法。此外,您不必以旧方式添加库,实际上使用 @import 更快更高效。如果您仍然在寻找不错的参考资料,我强烈建议您阅读 this article

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-03-15
        • 1970-01-01
        • 1970-01-01
        • 2010-09-24
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多