Какую условную компиляцию использовать для переключения между кодами 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).
Если вы на самом деле хотели использовать разные флаги в зависимости от версии ОС (потому что новая версия фактически добавила новые функциональные возможности, а не просто переименовывала константу), то есть две разумные вещи, которые вы можете сделать, ни одна из которых не выполняется вашим макросом:
Чтобы всегда использовать старые флаги, если разрешенная минимальная версия не превышает версию, в которой они были представлены (что-то вроде этого):
#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 40000 || __MAC_OS_X_VERSION_MIN_REQUIRED >= 1060) NSDataReadingOptions options = NSDataReadingMapped; #else NSDataReadingOptions options = NSMappedRead; #end
Условно используйте только новые значения в сборках, которые могут использоваться только в новых версиях, и скомпилируйте в коде, чтобы определить флаги во время выполнения для сборок, которые поддерживают обе версии:
#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