【问题标题】:Preprocessor macro for OS X targets?OS X目标的预处理器宏?
【发布时间】:2012-08-21 09:46:06
【问题描述】:

我有一个同时具有 iOS 和 OS X 目标的项目。当我为 OS X 编译时,是否有一个正确的预处理器宏?我试过这个:

#if TARGET_OS_MAC
@interface BFNode : NSObject <NSPasteboardReading, NSPasteboardWriting> {
#else
@interface BFNode : NSObject {
#endif

但是TARGET_OS_MAC 似乎不起作用。当我尝试在 iOS 上运行该应用程序时,我得到一个编译器错误,因为它尝试编译第一行并且 iOS 上没有定义 NSPasteboardReading 协议。

我知道还有TARGET_OS_IPHONE。如果我使用它并交换 @interface 声明,它就可以工作。

但是有很多地方我有代码,没有 iOS 版本,所以我也需要一个用于 OS X 的宏。

解决方案:

我最终在 .pch 文件中定义了一个新宏:

#define TARGET_OSX TARGET_OS_IPHONE == 0

【问题讨论】:

    标签: objective-c ios xcode macos cocoa


    【解决方案1】:

    根据TargetConditionals.h 中的文档(看来,截至 2020 年;任何平台):

    +---------------------------------------------------------------------+
    |                            TARGET_OS_MAC                            |
    | +---+ +-----------------------------------------------+ +---------+ |
    | |   | |               TARGET_OS_IPHONE                | |         | |
    | |   | | +---------------+ +----+ +-------+ +--------+ | |         | |
    | |   | | |      IOS      | |    | |       | |        | | |         | |
    | |OSX| | |+-------------+| | TV | | WATCH | | BRIDGE | | |DRIVERKIT| |
    | |   | | || MACCATALYST || |    | |       | |        | | |         | |
    | |   | | |+-------------+| |    | |       | |        | | |         | |
    | |   | | +---------------+ +----+ +-------+ +--------+ | |         | |
    | +---+ +-----------------------------------------------+ +---------+ |
    +---------------------------------------------------------------------+
    

    这告诉我们:

    • TARGET_OS_MAC 将是1 用于(可能)在 Apple 平台上运行的任何 Cocoa 应用程序。

      • TARGET_OS_OSX 仅适用于 macOS 目标的 1
      • 对于任何非 Mac Apple 产品,TARGET_OS_IPHONE 将是 1
        • TARGET_OS_IOS 仅适用于 iOS
          • TARGET_OS_MACCATALYST 仅适用于 Project Catalyst。看来TARGET_OS_UIKITFORMAC 也可以。
        • TARGET_OS_TV 仅适用于 tvOS
        • TARGET_OS_WATCH 仅适用于 watchOS
        • TARGET_OS_BRIDGE 仅适用于 bridgeOS(目前甚至不支持 3rd-party 应用程序,因此您可能总是会看到 0
      • 当为 DriverKit 构建时,TARGET_OS_DRIVERKIT 将是 1

    ⚠️ 但是等等! ⚠️

    我是从 iOS 14(macOS 11、watchOS 7)SDK 中获得的。如果我回顾 iOS 13(macOS 10.15,watchOS 6)SDK,我会看到:

    +----------------------------------------------------------------+
    |                TARGET_OS_MAC                                   |
    | +---+  +-----------------------------------------------------+ |
    | |   |  |          TARGET_OS_IPHONE                           | |
    | |OSX|  | +-----+ +----+ +-------+ +--------+ +-------------+ | |
    | |   |  | | IOS | | TV | | WATCH | | BRIDGE | | MACCATALYST | | |
    | |   |  | +-----+ +----+ +-------+ +--------+ +-------------+ | |
    | +---+  +-----------------------------------------------------+ |
    +----------------------------------------------------------------+
    

    值得注意的是,TARGET_OS_DRIVERKIT 是 14 中的新内容,TARGET_OS_MACCATALYST 现在在 IOS 中。这告诉我如果假定TARGET_OS_MACCATALYSTIOS 是完全独立的,则针对iOS 14 SDK 编译可能会破坏为iOS 13 编写的一些C 代码


    此外,还定义了这些:

    • TARGET_OS_SIMULATOR 仅适用于 iOS, tvOS, and watchOS simulators。您可以使用上面的#defines 进一步细化它
    • TARGET_OS_WIN32 以防您想使用 Apple 的 SDK 来制作 Windows 应用程序。除了 Apple 自己的(如 iTunes、Safari 和 QuickTime),我个人不知道其他任何东西。如果您想随身携带现有的 Objective-C 代码,现在 Swift 支持 Windows,这可能会变得有用。
    • TARGET_OS_UNIX 用于非 Apple UNIX 系统

    这些已被弃用,不应再使用。也就是说,您可能会在必须维护的代码中找到它们,因此它们的含义如下:

    • TARGET_IPHONE_SIMULATOR 以前表示iPhoneOS 模拟器
    • TARGET_OS_EMBEDDED 以前是指 iOS、tvOS 和 watchOS 非模拟设备
    • TARGET_OS_NANO 以前可能是指 iPod Nano(我在网上找不到任何历史用法)

    还有一点需要注意的是,the TargetConditionals.h used in swift-corelibs-foundation 明显不同,包括适用于 Android、Cygwin 和其他未明确支持但在技术上工作的平台的 #defines。

    我不完全确定该怎么做。我猜这是为了编译 Swift Foundation 框架,而不是为了使用它,因为 Swift 不使用 #defines。

    【讨论】:

    • 我提醒您不要相信头文件中的版权信息作为参考日期。 Apple Watch 甚至在 2014 年都不存在。
    • TARGET_OS_SIMULATOR 或许也应该被提及。
    • @Pavel 好主意!我已经添加了所有我认为值得注意的内容?
    • 太棒了!!但对于 C 和/或 C++ 语言,#include &lt;TargetConditionals.h&gt; 是必需的(由#ifdef __APPLE__ 检查包装),请参阅related post 了解更多信息。
    • @Top-Master 因为 Obj-C 是 C 的严格超集,所以这也适用。如果您按照这个问题的要求为 macOS 进行编译,那么您可以使用 TargetConditionals.h 标头 (MacOSX.sdk/usr/include/TargetConditionals.h),它只是纯 C 宏。事实上它是如此基础,我敢打赌 C89 编译器会很好地处理它
    【解决方案2】:

    如果您使用的是 Swift,这里有一个很棒的 language feature。如果您使用的是 Objective-C,那么执行以下操作通常很有用:

    #include <TargetConditionals.h>
    
    #if TARGET_OS_IPHONE
        @import UIKit;
    #else
        @import AppKit;
    #endif
    

    了解TARGET_OS_* 的定义将使这更有意义。最值得注意的是,TARGET_OS_MAC 是任何令人意想不到的 Apple 平台。 !TARGET_OS_IPHONE 是 macOS,TARGET_OS_IPHONE 是除此之外的任何东西。 iOS、tvOS 和 watchOS 有更具体的定义。

    来自TargetConditions.h

    TARGET_OS_* 
    These conditionals specify in which Operating System the generated code will
    run.  Indention is used to show which conditionals are evolutionary subclasses.  
    
    The MAC/WIN32/UNIX conditionals are mutually exclusive.
    The IOS/TV/WATCH conditionals are mutually exclusive.
    
    
        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
    
        TARGET_IPHONE_SIMULATOR   - DEPRECATED: Same as TARGET_OS_SIMULATOR
        TARGET_OS_NANO            - DEPRECATED: Same as TARGET_OS_WATCH
    

    【讨论】:

    【解决方案3】:

    那是因为TARGET_OS_MAC 也是在为 iOS 构建时定义的。

    请参阅http://sealiesoftware.com/blog/archive/2010/8/16/TargetConditionalsh.html

    我会尝试通过目标上的构建设置来构建自己的目标特定定义。

    【讨论】:

    • 也别忘了#include // Xcode 8+中需要
    【解决方案4】:

    从 Xcode 8 GM 开始,这是 iOS 10.0 中 TargetConditionals.h 的相关部分:

    #define TARGET_OS_MAC               1
    #define TARGET_OS_WIN32             0
    #define TARGET_OS_UNIX              0
    #define TARGET_OS_OSX               0
    #define TARGET_OS_IPHONE            1
    #define TARGET_OS_IOS               1
    #define TARGET_OS_WATCH             0
    #define TARGET_OS_BRIDGE            0
    #define TARGET_OS_TV                0
    #define TARGET_OS_SIMULATOR         0
    #define TARGET_OS_EMBEDDED          1 
    #define TARGET_IPHONE_SIMULATOR     TARGET_OS_SIMULATOR /* deprecated */
    #define TARGET_OS_NANO              TARGET_OS_WATCH /* deprecated */
    

    (在 Xcode 文本编辑器上方的“面包屑”中,iOS 10.0 &gt; usr/include &gt; TargetConditionals.h

    ...对于 macOS,这是同一个文件:

    #define TARGET_OS_MAC               1
    #define TARGET_OS_WIN32             0
    #define TARGET_OS_UNIX              0
    #define TARGET_OS_OSX               1
    #define TARGET_OS_IPHONE            0
    #define TARGET_OS_IOS               0
    #define TARGET_OS_WATCH             0
    #define TARGET_OS_BRIDGE            0
    #define TARGET_OS_TV                0
    #define TARGET_OS_SIMULATOR         0
    #define TARGET_OS_EMBEDDED          0 
    #define TARGET_IPHONE_SIMULATOR     TARGET_OS_SIMULATOR /* deprecated */
    #define TARGET_OS_NANO              TARGET_OS_WATCH /* deprecated */ 
    

    (在 Xcode 文本编辑器上方的“面包屑”中,macOS 10.12 &gt; usr/include &gt; TargetConditionals.h

    TARGET_OS_MAC 在两个平台上都定义为 1,但 TARGET_OS_OSX 仅在 macOS 上是 1

    在文件的cmets中,它们是这样描述的:

    • TARGET_OS_MAC:生成的代码将在 Mac OS X 变体下运行。
    • TARGET_OS_OSX:生成的代码将在 OS X 设备下运行。

    我猜当时的某个时候(也许是在 iPhone 发布的时候?)有人认为 iOS(né “iPhone OS”)符合“Mac OS X 变体”的定义。

    【讨论】:

    • 我记得史蒂夫乔布斯在 2007 年发布 iPhone 时曾在舞台上提到“它运行 Mac OS X”。
    【解决方案5】:

    在 TargetConditionals.h 中查找整个列表,包括:

    #define TARGET_OS_MAC               1
    #define TARGET_OS_IPHONE            1 
    #define TARGET_IPHONE_SIMULATOR     1 
    

    【讨论】:

    • 你似乎弄错了。实际上 TARGET_OS_MAC 是在为 iOS 构建时定义的。
    【解决方案6】:

    我会推荐使用这个:

    #define TARGET_OS_OSX (!(TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH))
    

    【讨论】:

    • 当时不是
    • 但是您在 2016 年回答...而我正在查看似乎是在 2014 年编写的 TargetConditionals.h?至少版权日期是这样写的:“Copyright (c) 2000-2014 by Apple Inc.”。
    • 不管怎样,故意在别人的命名空间中命名都是不好的做法。这就是你得到#SmooshGate的方式。
    • 是的,我在 2016 年回答过,我重申当时没有定义。您应该停止信任头文件中的版权信息。
    • 而 SmooshGate 的事情似乎完全不同。这不是一个实现,只是一个预处理器宏指令,如果重新定义会导致编译器警告。但是如果你想要一个与 SmooshGate 类似的案例,你应该检查无前缀的类别方法。
    【解决方案7】:

    我遇到了同样的情况,但 Swift 需要它。

    Apple 开发者论坛上的这个答案非常有助于在您的情况下处理 NSPasteboard 协议,但对于 Swift。

    https://forums.developer.apple.com/thread/16757

    我最终做了:

    #if os(iOS) || os(watchOS)
      public protocol TFPasteboardReading { }
      public protocol TFPasteboardWriting { }
    #elseif os(macOS)
      public typealias TFPasteboardReading=NSPasteboardReading
      public typealias TFPasteboardWriting=NSPasteboardWriting
    #endif
    
    
    @objc(TFCategory)
    public class TFCategory: TFBaseModel, TFPasteboardReading, TFPasteboardWriting {
    
    ...
    
    }
    

    这样TFPasteboardReadingTFPasteboardWriting是为macOS定义的,分别表示NSPasteboardReadingNSPasteboardWriting,在iOS中它没有意义,但在iOS中仍然可以引用。所以代码对两者都能正确编译。

    【讨论】:

      猜你喜欢
      • 2015-01-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-14
      • 2012-12-29
      • 2016-08-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多