简体   繁体   中英

Preprocessor macro for OS X targets?

I have a project that has both an iOS and an OS X target. Is there a preprocessor macro that is true when I'm compiling for OS X? I tried this:

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

But TARGET_OS_MAC doesn't seem to work. When I try to run the app on iOS I get a compiler error, because it tries to compile the first line and there is no NSPasteboardReading protocol defined on iOS.

I know there is TARGET_OS_IPHONE too. If I use that and swap the @interface declarations it works.

But there are a lot of places where I have code, that has no iOS version, so I need a macro for OS X too.

SOLUTION:

I ended up defining a new macro in the .pch file:

#define TARGET_OSX TARGET_OS_IPHONE == 0

As per the documentation in TargetConditionals.h (it seems, as of 2020; any platform):

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

This tells us:

  • TARGET_OS_MAC will be 1 for (probably) any Cocoa application running on an Apple platform.

    • TARGET_OS_OSX will only be 1 for macOS targets
    • TARGET_OS_IPHONE will be 1 for any non-Mac Apple products
      • TARGET_OS_IOS is just for iOS
        • TARGET_OS_MACCATALYST is just for Project Catalyst . It seems TARGET_OS_UIKITFORMAC will also work.
      • TARGET_OS_TV is just for tvOS
      • TARGET_OS_WATCH is just for watchOS
      • TARGET_OS_BRIDGE is just for bridgeOS (which currently doesn't even support 3rd-party apps so you'll likely always see that be 0 )
    • TARGET_OS_DRIVERKIT will be 1 when building for DriverKit

⚠️ But wait! ⚠️

I got that from the iOS 14 (macOS 11, watchOS 7) SDK. If I look back into the iOS 13 (macOS 10.15, watchOS 6) SDK, I see this:

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

Notably, TARGET_OS_DRIVERKIT is new in 14, and TARGET_OS_MACCATALYST is inside IOS now. This tells me that compiling against the iOS 14 SDK can break some C code written for iOS 13 , if it assumes that TARGET_OS_MACCATALYST and IOS are completely separate.


Additionally, these are defined:

  • TARGET_OS_SIMULATOR is just for iOS, tvOS, and watchOS simulators . You can further refine this using the above #define s
  • TARGET_OS_WIN32 is in case you wanna use Apple's SDKs to make Windows apps. I don't personally know of any other than Apple's own (like iTunes, Safari, and QuickTime). This might become useful now that Swift has Windows support, if you want to take existing Objective-C code with you.
  • TARGET_OS_UNIX is for non-Apple UNIX systems

And these are deprecated, and should not be used anymore. That said, you might find them in code you have to maintain, so here's what they meant:

  • TARGET_IPHONE_SIMULATOR used to mean the iPhoneOS simulator
  • TARGET_OS_EMBEDDED used to mean iOS, tvOS, and watchOS non-simulated devices
  • TARGET_OS_NANO probably used to mean iPod Nano (I can't find any historical usage online)

Something else to note is that the TargetConditionals.h used in swift-corelibs-foundation is significantly different, and includes #define s for Android, Cygwin, and other not-explicitly-supported-but-they-technically-work platforms.

I'm not entirely sure what to make of this. I would guess it's for compiling the Swift Foundation framework, and not for consuming it, since Swift doesn't consume #define s.

If you're using Swift, there's a great language feature for this. If you're using Objective-C, it's often useful to do something like this:

#include <TargetConditionals.h>

#if TARGET_OS_IPHONE
    @import UIKit;
#else
    @import AppKit;
#endif

Understanding the TARGET_OS_* defines will make this make a lot more sense. Most notably, TARGET_OS_MAC is any Apple platform which is pretty unexpected. !TARGET_OS_IPHONE is macOS and TARGET_OS_IPHONE is anything besides that. There are more specific defines for iOS, tvOS, and watchOS.

From 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

That is because TARGET_OS_MAC is defined when building for iOS as well.

See http://sealiesoftware.com/blog/archive/2010/8/16/TargetConditionalsh.html on that.

I would try and build my own target specific define via build-settings on the target.

As of Xcode 8 GM, this is the relevant section of TargetConditionals.h in iOS 10.0:

#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 */

(In the 'breadcrumb' above the text editor in Xcode, iOS 10.0 > usr/include > TargetConditionals.h )

...And this is the same file, for 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 */ 

(In the 'breadcrumb' above the text editor in Xcode, macOS 10.12 > usr/include > TargetConditionals.h )

TARGET_OS_MAC is defined as 1 on both platforms, but TARGET_OS_OSX is 1 only on macOS.

In the file's comments, they are described as follows:

  • TARGET_OS_MAC: Generated code will run under Mac OS X variant.
  • TARGET_OS_OSX: Generated code will run under OS X devices.

I guess at some point back then (perhaps around when the iPhone was announced?) somebody decided that iOS (né "iPhone OS") fits the definitions of "Mac OS X variant".

Look in TargetConditionals.h for an entire list, including:

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

我建议使用这个:

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

I ran into this same situation, but needed it for Swift.

This answer here on the Apple developer forums was very helpful in dealing with the NSPasteboard protocols in your situation, but for Swift.

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

I ended up doing:

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

...

}

That way TFPasteboardReading and TFPasteboardWriting is defined for macOS meaning NSPasteboardReading and NSPasteboardWriting respectively and in iOS it has no meaning, but can still be referenced in iOS. So the code compiles properly for both.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM