Какую условную компиляцию использовать для переключения между Mac и iPhone конкретным кодом? - PullRequest
39 голосов
/ 05 июля 2010

Я работаю над проектом, который включает в себя приложение Mac и приложение для iPad, которые совместно используют код. Как я могу использовать условные ключи компиляции, чтобы исключить специфичный для Mac код из проекта iPhone и наоборот? Я заметил, что TARGET_OS_IPHONE и TARGET_OS_MAC оба равны 1, и поэтому они оба всегда верны. Есть ли другой ключ, который я могу использовать, который будет возвращать true только при компиляции для конкретной цели?

По большей части я получил файлы для совместной работы, переместив #include <UIKit/UIKit.h> и #include <Cocoa/Cocoa.h> в заголовки прекомпиляции для двух проектов. Я делюсь моделями и некоторым служебным кодом, который выбирает данные из 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, что кажется неверным.

Заранее спасибо за любую помощь!

Ответы [ 4 ]

65 голосов
/ 06 июля 2010

Вы допустили ошибку в своих наблюдениях.:)

TARGET_OS_MAC будет 1 при создании приложения для Mac или iPhone.Вы правы, это совершенно бесполезно для такого рода вещей.

Однако, TARGET_OS_IPHONE равно 0 при создании приложения для Mac.Для этой цели я все время использую TARGET_OS_IPHONE в своих заголовках.

Вот так:

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

Вот отличная таблица на этом: http://sealiesoftware.com/blog/archive/2010/8/16/TargetConditionalsh.html

8 голосов
/ 05 июля 2010

"Правильнее всего использовать новые константы, потому что если вы посмотрите на заголовок, вы увидите, что они объявлены эквивалентными старым в перечислении, что означает, что новые константы будут работать даже в старых выпусках (обе константы компилируются в одну и ту же вещь, и поскольку перечисления скомпилированы в приложение, они не могут измениться без нарушения бинарной совместимости.) Единственная причина, по которой это не нужно делать, - это если вам нужно продолжить сборку старых SDK (то есть это не то же самое, что поддержка старых выпусков, что можно сделать при компиляции с новыми SDK).

Если вы на самом деле хотели использовать разные флаги в зависимости от версии ОС (потому что новая версия фактически добавила новые функциональные возможности, а не просто переименовывает константу), то есть две разумные вещи, которые вы можете сделать, ни одна из которых не является вашим макросом выше осуществляет:

  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"]. Вы также, как правило, хотите явно тестировать функции, используя слабые ссылки вместо сравнения версий, но это не вариант для перечислений.

7 голосов
/ 05 июля 2016

Используемые макросы определены в заголовочном файле SDK TargetConditionals.h. Взято из SDK 10.11:

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+?) Теперь имеют TARGET_OS_OSX, определенный специально для macOS. (h / t @OldHorse), так что это должно работать:

#if TARGET_OS_OSX
 // macOS-only code
#endif
2 голосов
/ 08 марта 2017

Набор используемых макросов теперь включает 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.

...