iphone xcode macos conditional-compilation

¿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:

  1. 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

  2. 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.