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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3181321/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 03:27:16  来源:igfitidea点击:

Which conditional compile to use to switch between Mac and iPhone specific code?

iphonexcodemacosconditional-compilation

提问by Jose Ibanez

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_IPHONEand TARGET_OS_MACare 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?

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

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.

在大多数情况下,我通过移动#include <UIKit/UIKit.h>#include <Cocoa/Cocoa.h>进入两个项目的预编译头文件来使文件协作。我正在分享模型和一些从 RSS 提要和 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:

特别是,该[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))

#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.

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

Thanks in advance for any help!

在此先感谢您的帮助!

回答by Steven Fisher

You've made a mistake in your observations. :)

你在观察中犯了一个错误。:)

TARGET_OS_MACwill be 1 when building a Mac or iPhone application. You're right, it's quite useless for this sort of thing.

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

However, TARGET_OS_IPHONEis 0 when building a Mac application. I use TARGET_OS_IPHONEin my headers all the time for this purpose.

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

Like this:

像这样:

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

Here's a great chart on this: http://sealiesoftware.com/blog/archive/2010/8/16/TargetConditionalsh.html

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

回答by Louis Gerbarg

"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).

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

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:

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

  1. To always use the old flags unless the min version allowed is greater than version they were introduced in (something like this):

    #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 40000 || __MAC_OS_X_VERSION_MIN_REQUIRED >= 1060)
      NSDataReadingOptions  options = NSDataReadingMapped;
    #else
      NSDataReadingOptions  options = NSMappedRead;
    #end
    
  2. 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:

    #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
    
  1. 除非允许的最小版本大于引入的版本,否则始终使用旧标志(类似这样):

    #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 40000 || __MAC_OS_X_VERSION_MIN_REQUIRED >= 1060)
      NSDataReadingOptions  options = NSDataReadingMapped;
    #else
      NSDataReadingOptions  options = NSMappedRead;
    #end
    
  2. 有条件地仅使用只能在新版本上运行的构建中的新值,并在代码中编译以确定运行时支持两个版本的构建的标志:

    #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
    

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.

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

回答by Demitri

The macros to use are defined in the SDK header file TargetConditionals.h. Taken from the 10.11 SDK:

要使用的宏在 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

Since everything is a “Mac OS X variant” here, TARGET_OS_MACis not useful in this case. To compile specifically for macOS, for example:

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

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

Update: Newer headers (Xcode 8+?) now have TARGET_OS_OSXdefined specifically for macOS. (h/t @OldHorse), so this should work:

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

#if TARGET_OS_OSX
 // macOS-only code
#endif

回答by OldHorse

The set of macros to use includes now TARGET_OS_OSX:

要使用的宏集现在包括 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

Seems to work ok for conditional compilation of macOS code.

似乎适用于 macOS 代码的条件编译。