Какая условная компиляция используется для переключения между конкретным кодом Mac и iPhone?
Я работаю над проектом, который включает приложение Mac и приложение iPad, совместно использующее код. Как я могу использовать условные компиляционные ключи для исключения кода для Mac из проекта iPhone и наоборот? Я заметил, что TARGET_OS_IPHONE
и TARGET_OS_MAC
равны 1, и поэтому оба они всегда верны. Есть ли другой переключатель, который я могу использовать, который вернет true только при компиляции для определенной цели?
По большей части, я получил файлы к сотрудничеству, переместив #include <UIKit/UIKit.h>
и #include <Cocoa/Cocoa.h>
в заголовки precompile для двух проектов. Я делюсь моделями и некоторым кодом утилиты, который извлекает данные из 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, что кажется неправильным.
Заранее благодарим за помощь!
Ответы
Ответ 1
Вы сделали ошибку в своих наблюдениях.:)
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
Ответ 2
"Правильная вещь - просто использовать новые константы, потому что если вы посмотрите на заголовок, вы увидите, что они объявлены эквивалентными старым в перечислении, что означает, что новые константы будут работать даже на старых версиях (обе константы компилируются в одно и то же, а так как перечисления скомпилированы в приложение, которое они не могут изменить, не нарушая двоичную совместимость). Единственная причина, по которой это не делать, - это продолжить восстановление старых SDK (это отличная от поддержки старых версий, которые вы можете выполнять при компиляции с новыми SDK).
Если вы действительно хотели использовать разные флаги на основе версии ОС (потому что новая версия фактически добавила новые функции, а не просто переименование константы), то есть две разумные вещи, которые вы можете сделать, ни один из которых ваш макрос выше выполняет:
-
Чтобы всегда использовать старые флаги, если только разрешенная версия min больше версии, они были введены в (что-то вроде этого):
#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"]
. Вы также обычно хотите явно тестировать функции, используя такие вещи, как слабое связывание, а не сравнение версий, но это не вариант для перечислений.
Ответ 3
Используемые макросы определены в файле заголовка 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
Ответ 4
В набор используемых макросов теперь входит 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.