2010-07-05 16 views
37

Estoy trabajando en un proyecto que incluye una aplicación Mac y una aplicación para 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?¿Qué compilación condicional usar para cambiar entre el código específico de Mac y iPhone?

En la mayoría de los casos, obtuve los archivos para cooperar moviendo #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 anterior, y Mac OS 10.5 y anterior 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 yo quiero para asegurarse de que esto es 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!

Respuesta

64

Ha cometido un error en sus observaciones. :)

TARGET_OS_MAC será 1 al construir 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 cuando la construcción de una aplicación de Mac. Uso TARGET_OS_IPHONE en mis encabezados todo el tiempo para este propósito.

De esta manera:

#if TARGET_OS_IPHONE 
// iOS code 
#else 
// OSX code 
#endif 

Aquí está una gran tabla en esto: http://sealiesoftware.com/blog/archive/2010/8/16/TargetConditionalsh.html

+0

Desafortunadamente, TARGET_OS_IPHONE parece estar definido en cualquiera de los casos, si usted tiene un proyecto tanto para iOS y OSX. –

+5

Sí. Se define como 0 para OSX, 1 para iOS. Necesitas usar '#if TARGET_OS_IPHONE', no' #ifdef TARGET_OS_IPHONE'. Se agregó un ejemplo. –

+0

Por cierto, la tabla es del "controlador de tiempo de ejecución" de Apple. Si la realidad parece estar en desacuerdo con él, cuestiona la realidad. :) –

7

"Lo correcto es usar las nuevas constantes, porque si miras el encabezado verás que son declaradas equivalentes a las anteriores en la enumeración, lo que significa que las nuevas constantes funcionarán incluso en el antiguo versiones (ambas constantes se compilan para lo mismo, y como 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 (es decir, una cosa diferente a la compatibilidad con versiones anteriores, que puede hacer compilando con los SDK más nuevos).

Si realmente desea usar diferentes indicadores basados ​​en la versión del sistema operativo (porque la nueva versión agrega realmente nuevas funcionalidades, en lugar de simplemente cambiar el nombre de una constante), entonces hay dos cosas sensibles que puede hacer, ninguno de los cuales la macro anterior logra:

  1. Para utilizar siempre las viejas banderas a menos que la versión min permitido es mayor que la versión que se introdujeron en (algo así):

    #if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 40000 || __MAC_OS_X_VERSION_MIN_REQUIRED >= 1060) 
        NSDataReadingOptions options = NSDataReadingMapped; 
    #else 
        NSDataReadingOptions options = NSMappedRead; 
    #end 
    
  2. usar sólo condicionalmente los nuevos valores en generaciones que lata solamente en las nuevas versiones, y compilar el código para determinar las banderas en tiempo de ejecución de generaciones que soporta 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 muchas veces, querría esconder el resultado del [[UIDevice currentDevice] systemVersion] compare:@"4.0"] en 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.

+0

Gracias! Esto es bueno, pero creo que todavía podría haber algunos problemas potenciales aquí. En las Opciones de compilación para el objetivo, hay dos configuraciones separadas en Despliegue, Objetivo de despliegue de Mac OS X y Destino de despliegue de SO de iPhone. En ambos ejemplos, si el Mac OS X Destino de despliegue se establece en Mac OS X 10.6, utilizará la nueva enumeración, incluso si usted está construyendo para el iPhone OS 3.2. ¿Hay alguna manera, en tiempo de ejecución o de otro tipo, para determinar a qué sistema operativo se está apuntando? –

+0

Estás confundiendo la configuración del inspector de Xcode con lo que se envía en realidad para el compilador. Ambos campos están disponibles porque se pueden crear algunos tipos de objetivos (como bibliotecas estáticas) para ambas plataformas, pero solo se usa el campo relevante para la plataforma que se está construyendo. Nunca se necesita para determinar en tiempo de ejecución, que está en la mira del sistema operativo, que conocen el sistema operativo en tiempo de compilación (que no son compatibles a nivel binario, y utilizan diferentes procesadores). Solo necesita determinar en tiempo de ejecución entre las versiones del mismo SO, ya que un solo binario podría ejecutarse contra diferentes versiones. –

5

Las macros para usar se definen en el archivo de cabecera TargetConditionals.h SDK. Tomado del 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 

Ya que 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 
0

El conjunto de macros para uso 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 de MacOS.

Cuestiones relacionadas