使用哪个条件编译来在 Mac 和 iPhone 特定代码之间切换?
我正在开发一个项目,其中包括共享代码的 Mac 应用程序和 iPad 应用程序。如何使用条件编译开关从 iPhone 项目中排除 Mac 特定的代码,反之亦然?我注意到 TARGET_OS_IPHONE
和 TARGET_OS_MAC
都是 1,所以它们总是 true。是否还有另一个我可以使用的开关,它仅在针对特定目标进行编译时返回 true?
在大多数情况下,我通过移动 #include
和 #include
来使文件进行协作到两个项目的预编译头中。我正在分享模型和一些从 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编译的,它仍然会使用新常量,这似乎不正确。
预先感谢您的任何帮助!
I am working on a project that includes a Mac application and an iPad application that share code. How can I use conditional compile switches to exclude Mac-specific code from the iPhone project and vice-versa? I've noticed that TARGET_OS_IPHONE
and TARGET_OS_MAC
are both 1, and so they are both always true. Is there another switch I can use that will only return true when compiling for a specific target?
For the most part, I've gotten the files to cooperate by moving #include <UIKit/UIKit.h>
and #include <Cocoa/Cocoa.h>
into the precompile headers for the two projects. I'm sharing models and some utility code that fetches data from RSS feeds and Evernote.
In particular, the [NSData dataWithContentsOfURL:options:error:]
function takes a different constant for the options parameter iOS 3.2 and earlier and Mac OS 10.5 and earlier than it does for iOS 4 and Mac OS 10.6. The conditional I'm using is:
#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))
This seems to work, but I want to make sure this is bulletproof. My understanding is that if the Mac version is set to 10.6, but the iOS version is set to 3.2, it will still use the new constants even if it's compiling for iOS 3.2, which seems incorrect.
Thanks in advance for any help!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
你的观察犯了错误。 :)
构建 Mac 或 iPhone 应用程序时,
TARGET_OS_MAC
将为 1。你说得对,对于这种事情来说,这是毫无用处的。但是,在构建 Mac 应用程序时,
TARGET_OS_IPHONE
为 0。为此,我一直在标头中使用TARGET_OS_IPHONE
。像这样:
这是一个很棒的图表:
http://sealiesoftware.com/blog/archive/2010/8/16 /TargetConditionalsh.html
You've made a mistake in your observations. :)
TARGET_OS_MAC
will be 1 when building a Mac or iPhone application. You're right, it's quite useless for this sort of thing.However,
TARGET_OS_IPHONE
is 0 when building a Mac application. I useTARGET_OS_IPHONE
in my headers all the time for this purpose.Like this:
Here's a great chart on this:
http://sealiesoftware.com/blog/archive/2010/8/16/TargetConditionalsh.html
要使用的宏在 SDK 头文件
TargetConditionals.h
中定义。取自 10.11 SDK:由于这里的所有内容都是“Mac OS X 变体”,因此
TARGET_OS_MAC
在这种情况下没有用处。要专门针对 macOS 进行编译,例如:更新:较新的标头(Xcode 8+?)现在专门为 macOS 定义了
TARGET_OS_OSX
。 (h/t @OldHorse),所以这应该有效:The macros to use are defined in the SDK header file
TargetConditionals.h
. Taken from the 10.11 SDK:Since everything is a “Mac OS X variant” here,
TARGET_OS_MAC
is not useful in this case. To compile specifically for macOS, for example:Update: Newer headers (Xcode 8+?) now have
TARGET_OS_OSX
defined specifically for macOS. (h/t @OldHorse), so this should work:“正确的做法是使用较新的常量,因为如果您查看标头,您会发现它们被声明为与枚举中的旧常量等效,这意味着新常量甚至可以在旧版本上工作(这两个常量编译为相同的东西,并且由于枚举被编译到应用程序中,因此它们无法在不破坏二进制兼容性的情况下进行更改,不这样做的唯一原因是如果您需要继续构建较旧的 SDK(这与旧版 SDK 不同)。支持旧版本,您可以在针对较新的 SDK 进行编译时执行此操作。
如果您确实想根据操作系统版本使用不同的标志(因为新版本实际上添加了新功能,而不是仅仅重命名常量),那么可以使用以下方法。您可以做两件明智的事情,上面的宏都无法完成:
始终使用旧标志,除非允许的最小版本大于它们引入的版本(类似这样):
有条件地仅在只能在新版本上使用的构建中使用新值版本,并在代码中进行编译以确定运行时支持这两个版本的构建的标志:
请注意,如果您实际上正在进行此比较很多时候,您希望将
[[UIDevice currentDevice] systemVersion] Compare:@"4.0"]
的结果存储在某处。您通常还希望使用弱链接等方式显式测试功能,而不是进行版本比较,但这不是枚举的选项。"The correct thing to do is just use the newer constants, because if you look at the header you will see they are declared equivalent to the old ones in the enum, which means the new constants will work even on the old releases (both constants compile to the same thing, and since enums are compiled into the app they can't change without breaking binary compatibility). The only reason not to do that is if you need to continue building agains the older SDKs (that is a different thing than supporting older releases, which you can do while compiling against the newer SDKs).
If you actually wanted to use different flags based on the OS version (because the new version actually added new functionality, as opposed to just renaming a constant) then there are two sensible things you can do, neither of which your above macro accomplishes:
To always use the old flags unless the min version allowed is greater than version they were introduced in (something like this):
Conditionally use only the new values in builds that can on only the new versions, and compile in code to determine the flags at runtime for builds that support both versions:
Note that if you actually were doing this comparison a lot you would want to stash the result of the
[[UIDevice currentDevice] systemVersion] compare:@"4.0"]
somewhere. You also generally want to explicitly test for features using things like weak linking instead of doing version compares, but that is not an option for enums.要使用的宏集现在包括 TARGET_OS_OSX:
对于 macOS 代码的条件编译似乎可以正常工作。
The set of macros to use includes now TARGET_OS_OSX:
Seems to work ok for conditional compilation of macOS code.
不适用于此 Cocoa 问题,但对于新读者,您可以在 Swift 项目上使用:
Does not apply to this Cocoa question but, for new readers, on Swift projects you can use: