哪个条件编译用于在 Mac 和 iPhone 特定代码之间切换?

Posted

技术标签:

【中文标题】哪个条件编译用于在 Mac 和 iPhone 特定代码之间切换?【英文标题】:Which conditional compile to use to switch between Mac and iPhone specific code? 【发布时间】:2011-03-12 00:19:05 【问题描述】:

我正在开发一个项目,其中包括一个共享代码的 Mac 应用程序和一个 iPad 应用程序。如何使用条件编译开关从 iPhone 项目中排除特定于 Mac 的代码,反之亦然?我注意到TARGET_OS_IPHONETARGET_OS_MAC 都是1,所以它们总是正确的。我可以使用另一个仅在为特定目标编译时返回 true 的开关吗?

在大多数情况下,我通过将#include <UIKit/UIKit.h>#include <Cocoa/Cocoa.h> 移动到两个项目的预编译头文件中来使文件相互配合。我正在共享模型和一些从 RSS 提要和 Evernote 获取数据的实用程序代码。

特别是,[NSData dataWithContentsOfURL:options:error:] 函数对选项参数 ios 3.2 和更早版本以及 Mac OS 10.5 和更早版本采用不同的常量,而不是在 iOS 4 和 Mac OS 10.6 中使用。我使用的条件是:

#if (TARGET_OS_IPHONE && (__IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_3_2)) || (TARGET_OS_MAC && (MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_5))

这似乎可行,但我想确保这是防弹的。我的理解是,如果 Mac 版本设置为 10.6,而 iOS 版本设置为 3.2,即使编译为 iOS 3.2,它仍然会使用新的常量,这似乎是不正确的。

提前感谢您的帮助!

【问题讨论】:

【参考方案1】:

您的观察有误。 :)

TARGET_OS_MAC 在构建 Mac 或 iPhone 应用程序时将为 1。你说得对,这种东西根本没用。

但是,TARGET_OS_IPHONE 在构建 Mac 应用程序时为 0。为此,我一直在标题中使用TARGET_OS_IPHONE

像这样:

#if TARGET_OS_IPHONE
// iOS code
#else
// OSX code
#endif

这是一张很棒的图表: http://sealiesoftware.com/blog/archive/2010/8/16/TargetConditionalsh.html

【讨论】:

不幸的是,如果您有适用于 iOS 和 OSX 的项目,则似乎在任何一种情况下都定义了 TARGET_OS_IPHONE。 是的。 OSX 定义为 0,iOS 定义为 1。您需要使用#if TARGET_OS_IPHONE,而不是#ifdef TARGET_OS_IPHONE。添加了一个示例。 顺便说一句,该图表来自 Apple 的“运行时牧马人”。如果现实似乎不同意他,质疑现实。 :)【参考方案2】:

“正确的做法是使用较新的常量,因为如果您查看标题,您会发现它们被声明为与枚举中的旧常量等效,这意味着即使在旧版本上新常量也可以工作(两个常量编译成同一个东西,并且由于枚举被编译到应用程序中,它们不能在不破坏二进制兼容性的情况下进行更改)。不这样做的唯一原因是如果您需要继续构建旧的 SDK(即与支持旧版本不同,您可以在针对较新的 SDK 进行编译时执行此操作)。

如果你真的想根据操作系统版本使用不同的标志(因为新版本实际上添加了新功能,而不是仅仅重命名一个常量)那么你可以做两件明智的事情,你上面的宏都不是完成:

    要始终使用旧标志,除非允许的最低版本大于引入它们的版本(类似这样):

    #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 40000 || __MAC_OS_X_VERSION_MIN_REQUIRED >= 1060)
      NSDataReadingOptions  options = NSDataReadingMapped;
    #else
      NSDataReadingOptions  options = NSMappedRead;
    #end
    

    有条件地仅在只能在新版本上使用的构建中使用新值,并在代码中编译以确定支持这两个版本的构建在运行时的标志:

    #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 40000 || __MAC_OS_X_VERSION_MIN_REQUIRED >= 1060)
      NSDataReadingOptions  options = NSDataReadingMapped;
    #else
      NSDataReadingOptions  options;
      if ([[UIDevice currentDevice] systemVersion] compare:@"4.0"] != NSOrderedAscending) 
         options = NSDataReadingMapped;
       else 
        options = NSMappedRead;
      
    #end
    

请注意,如果您确实经常进行此比较,您可能希望将[[UIDevice currentDevice] systemVersion] compare:@"4.0"] 的结果存储在某处。您通常还希望使用弱链接等方式显式测试功能,而不是进行版本比较,但这不是枚举的选项。

【讨论】:

谢谢!这是好东西,但我认为这里仍然可能存在一些潜在问题。在目标的构建选项中,部署下有两个单独的设置,Mac OS X 部署目标和 iPhone OS 部署目标。在这两个示例中,如果 Mac OS X 部署目标设置为 Mac OS X 10.6,它将使用新的枚举,即使您正在为 iPhone OS 3.2 构建。有没有办法在运行时或其他情况下确定目标操作系统? 您将 Xcode 检查器中的设置与实际发送到编译器的设置混淆了。这两个字段都可用,因为可以为这两个平台构建某些类型的目标(如静态库),但仅使用与正在构建的平台相关的字段。您永远不需要在运行时确定目标操作系统,您在编译时就知道操作系统(它们不是二进制兼容的,并且使用不同的处理器)。您只需要在运行时确定同一操作系统的不同版本,因为单个二进制文件可以针对不同的版本运行。【参考方案3】:

要使用的宏在 SDK 头文件 TargetConditionals.h 中定义。取自 10.11 SDK:

TARGET_OS_WIN32           - Generated code will run under 32-bit Windows
TARGET_OS_UNIX            - Generated code will run under some Unix (not OSX) 
TARGET_OS_MAC             - Generated code will run under Mac OS X variant
   TARGET_OS_IPHONE          - Generated code for firmware, devices, or simulator 
      TARGET_OS_IOS             - Generated code will run under iOS 
      TARGET_OS_TV              - Generated code will run under Apple TV OS
      TARGET_OS_WATCH           - Generated code will run under Apple Watch OS
   TARGET_OS_SIMULATOR      - Generated code will run under a simulator
   TARGET_OS_EMBEDDED       - Generated code for firmware

由于这里的一切都是“Mac OS X 变体”,TARGET_OS_MAC 在这种情况下没有用处。专门为 macOS 编译,例如:

#if !TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR && !TARGET_OS_EMBEDDED
    // macOS-only code
#endif

更新:较新的标头(Xcode 8+?)现在有专门为 macOS 定义的 TARGET_OS_OSX。 (h/t @OldHorse),所以这应该有效:

#if TARGET_OS_OSX
 // macOS-only code
#endif

【讨论】:

对我不起作用。 Xcode 12.4。它只会始终使用仅 MacOS 的代码。【参考方案4】:

要使用的宏集现在包括 TARGET_OS_OSX:

    TARGET_OS_WIN32           - Generated code will run under 32-bit Windows
    TARGET_OS_UNIX            - Generated code will run under some Unix (not OSX) 
    TARGET_OS_MAC             - Generated code will run under Mac OS X variant
       TARGET_OS_OSX          - Generated code will run under OS X devices
       TARGET_OS_IPHONE          - Generated code for firmware, devices, or simulator
          TARGET_OS_IOS             - Generated code will run under iOS 
          TARGET_OS_TV              - Generated code will run under Apple TV OS
          TARGET_OS_WATCH           - Generated code will run under Apple Watch OS
             TARGET_OS_BRIDGE          - Generated code will run under Bridge devices
       TARGET_OS_SIMULATOR      - Generated code will run under a simulator
       TARGET_OS_EMBEDDED       - Generated code for firmware

对于 macOS 代码的条件编译似乎可以正常工作。

【讨论】:

在 Xcode 12.4 中也不适合我。在通用的#if #else 语句中,它始终默认为 iOS 代码,即 else 情况,而不是使用 OSX 代码。

以上是关于哪个条件编译用于在 Mac 和 iPhone 特定代码之间切换?的主要内容,如果未能解决你的问题,请参考以下文章

CSS 径向渐变不适用于 iPhone 和 Mac

如何从早期版本升级到Ubuntu 17.04

iPhone 5 连接到 Mac,开发菜单选项显示设备的“用于开发”

iPhone X 在哪个国家最畅销?苹果终于被警告了

SwiftUI:单一目标中的 iPhone、iPad、Mac

iphone查看网络请求