哪个条件编译用于在 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_IPHONE
和TARGET_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 特定代码之间切换?的主要内容,如果未能解决你的问题,请参考以下文章
iPhone 5 连接到 Mac,开发菜单选项显示设备的“用于开发”