web-dev-qa-db-ja.com

MacとiPhone固有のコードを切り替えるために使用する条件付きコンパイルはどれですか?

私は、コードを共有するMacアプリケーションとiPadアプリケーションを含むプロジェクトに取り組んでいます。条件付きコンパイルスイッチを使用して、Mac固有のコードをiPhoneプロジェクトから除外したり、その逆を行うにはどうすればよいですか? _TARGET_OS_IPHONE_と_TARGET_OS_MAC_はどちらも1であるため、どちらも常にtrueであることに気付きました。特定のターゲット用にコンパイルするときにのみtrueを返す、使用できる別のスイッチはありますか?

ほとんどの場合、_#include <UIKit/UIKit.h>_と_#include <Cocoa/Cocoa.h>_を2つのプロジェクトのプリコンパイルヘッダーに移動することで、ファイルを連携させることができます。 RSSフィードとEvernoteからデータをフェッチするモデルといくつかのユーティリティコードを共有しています。

特に、_[NSData dataWithContentsOfURL:options:error:]_関数は、iOS 4およびMac OS 10.6の場合とは異なり、オプションパラメーターiOS 3.2以前およびMac OS 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))

これは動作するようですが、これが完全なものであることを確認したいと思います。私の理解では、Macのバージョンが10.6に設定されていて、iOSのバージョンが3.2に設定されている場合、iOS 3.2用にコンパイルされていても、新しい定数が使用されます。これは正しくないようです。

助けてくれてありがとう!

41
Jose Ibanez

観察を間違えました。 :)

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

67
Steven Fisher

使用するマクロは、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
8
Demitri

「正しいことは、新しい定数を使用することだけです。ヘッダーを見ると、列挙型の古い定数と同等に宣言されていることがわかります。つまり、新しい定数は古いリリースでも動作します(両方の定数)同じものにコンパイルします。列挙型はアプリにコンパイルされるため、バイナリ互換性を損なうことなく変更することはできません。これを行わない唯一の理由は、古いSDKを再度ビルドし続ける必要がある場合です(これは、古いリリースをサポートします。これは、新しいSDKに対してコンパイルするときに実行できます)。

OSバージョンに基づいて異なるフラグを実際に使用したい場合(新しいバージョンでは、定数の名前を変更するだけでなく、実際に新しい機能が追加されたため)、2つの賢明な方法があり、どちらも上記のマクロでは実行できません。

  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
    

実際にこの比較を頻繁に行っている場合は、[[UIDevice currentDevice] systemVersion] compare:@"4.0"]の結果をどこかに隠しておく必要があることに注意してください。また、バージョン比較を行う代わりに、弱リンクなどを使用して機能を明示的にテストしたい場合もありますが、これは列挙型のオプションではありません。

8
Louis Gerbarg

使用する一連のマクロに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コードの条件付きコンパイルでは問題なく動作するようです。

3
OldHorse