¿Qué compilación condicional usar para cambiar entre los códigos específicos de Mac y iPhone?
xcode macos (4)
Estoy trabajando en un proyecto que incluye una aplicación Mac y una aplicación iPad que comparten código. ¿Cómo puedo usar los modificadores de compilación condicionales para excluir el código específico de Mac del proyecto de iPhone y viceversa? Me he dado cuenta de que TARGET_OS_IPHONE
y TARGET_OS_MAC
son ambos 1, por lo que ambos son siempre ciertos. ¿Hay algún otro conmutador que pueda usar que solo se vuelva verdadero al compilar para un objetivo específico?
En su mayor parte, he conseguido que los archivos cooperen al mover #include <UIKit/UIKit.h>
y #include <Cocoa/Cocoa.h>
en los encabezados de precompilación para los dos proyectos. Estoy compartiendo modelos y algunos códigos de utilidad que obtienen datos de feeds RSS y Evernote.
En particular, la función [NSData dataWithContentsOfURL:options:error:]
toma una constante diferente para el parámetro de opciones iOS 3.2 y versiones anteriores y Mac OS 10.5 y anteriores que para iOS 4 y Mac OS 10.6. El condicional que estoy usando es:
#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))
Esto parece funcionar, pero quiero asegurarme de que sea a prueba de balas. Según entiendo, si la versión de Mac está configurada en 10.6, pero la versión de iOS está configurada en 3.2, seguirá usando las nuevas constantes, incluso si está compilando para iOS 3.2, lo que parece incorrecto.
¡Gracias de antemano por cualquier ayuda!
"Lo correcto es usar las constantes más nuevas, porque si se mira el encabezado verá que se declaran equivalentes a las antiguas en la enumeración, lo que significa que las nuevas constantes funcionarán incluso en las versiones anteriores (ambas constantes) compilación de lo mismo, y dado que las enumeraciones se compilan en la aplicación no pueden cambiar sin romper la compatibilidad binaria). La única razón para no hacer eso es si necesita continuar compilando los SDK anteriores (eso es algo diferente de compatibilidad con versiones anteriores, que puede hacer compilando con los nuevos SDK).
Si realmente quería usar banderas diferentes basadas en la versión del sistema operativo (porque la nueva versión realmente agrega una nueva funcionalidad, en lugar de solo renombrar una constante), hay dos cosas sensatas que puede hacer, ninguna de las cuales la macro anterior logra:
Para utilizar siempre las banderas antiguas a menos que la versión mínima permitida sea mayor que la versión en la que se introdujeron (algo como esto):
#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 40000 || __MAC_OS_X_VERSION_MIN_REQUIRED >= 1060) NSDataReadingOptions options = NSDataReadingMapped; #else NSDataReadingOptions options = NSMappedRead; #end
Condicionalmente, use solo los nuevos valores en compilaciones que solo puedan en las nuevas versiones, y compile en código para determinar los indicadores en tiempo de ejecución para compilaciones que admitan ambas versiones:
#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
Tenga en cuenta que si realmente estaba haciendo esta comparación, querría esconder el resultado del [[UIDevice currentDevice] systemVersion] compare:@"4.0"]
alguna parte. También, en general, quiere probar explícitamente las características que utilizan elementos como el enlace débil en lugar de hacer comparaciones de versiones, pero esa no es una opción para las enumeraciones.
Has cometido un error en tus observaciones. :)
TARGET_OS_MAC
será 1 cuando construyas una aplicación para Mac o iPhone. Tienes razón, es bastante inútil para este tipo de cosas.
Sin embargo, TARGET_OS_IPHONE
es 0 al TARGET_OS_IPHONE
una aplicación Mac. Uso TARGET_OS_IPHONE
en mis encabezados todo el tiempo para este propósito.
Me gusta esto:
#if TARGET_OS_IPHONE
// iOS code
#else
// OSX code
#endif
Aquí hay un gran cuadro sobre esto: http://sealiesoftware.com/blog/archive/2010/8/16/TargetConditionalsh.html
Las macros a usar se definen en el archivo de encabezado SDK TargetConditionals.h. Tomado del 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
Como todo es una "variante de Mac OS X" aquí, TARGET_OS_MAC
no es útil en este caso. Para compilar específicamente para macOS, por ejemplo:
#if !TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR && !TARGET_OS_EMBEDDED
// macOS-only code
#endif
El conjunto de macros a utilizar incluye ahora 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
Parece funcionar bien para la compilación condicional de código macOS.