Какую условную компиляцию использовать для переключения между кодами Mac и iPhone?

Я работаю над проектом, который включает в себя приложение 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, что кажется неверным.

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

5 ответов

Решение

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

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

Используемые макросы определены в заголовочном файле 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

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

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

Не относится к этому вопросу какао, но для новых читателей в проектах Swift вы можете использовать:

      #if os(macOS)
    // Compiles for macOS
#elseif os(iOS)
    // Compiles for iOS / iPadOS
#endif
Другие вопросы по тегам