iphone - ¿Hay alguna manera de que XCode advierta sobre nuevas llamadas API?
ios iphone-sdk-3.0 (9)
De hecho, he lanzado algo que ayuda a probar este tipo de cosas. Es parte de mi conjunto de clases de MJGAvailability.h llamado MJGAvailability.h .
La forma en que lo he estado usando es aplicarlo en mi archivo PCH de esta manera:
#define __IPHONE_OS_VERSION_SOFT_MAX_REQUIRED __IPHONE_4_0
#import "MJGAvailability.h"
// The rest of your prefix header as normal
#import <UIKit/UIKit.h>
Luego advertirá (con quizás una advertencia de desaprobación extraña) sobre las API que se están utilizando que son demasiado nuevas para el objetivo que establezca como el "máximo #define __IPHONE_OS_VERSION_SOFT_MAX_REQUIRED
" según el #define __IPHONE_OS_VERSION_SOFT_MAX_REQUIRED
. Además, si no define __IPHONE_OS_VERSION_SOFT_MAX_REQUIRED
, su valor predeterminado es su destino de despliegue.
Lo encuentro útil porque puedo verificar nuevamente qué API estoy usando que son demasiado nuevas para el destino de implementación que he configurado.
En más de una ocasión, he visto errores que aparecen en iOS 3.x debido al uso de una nueva llamada que se introdujo en 4.x sin una comprobación adecuada.
¿Hay alguna manera de que XCode advierta sobre clases, métodos y procedimientos que solo están disponibles en una versión posterior a la del objetivo de despliegue?
De esa forma podría enumerar fácilmente todo el código y asegurarme de que esté debidamente condicionalizado.
Después de buscar en AvailabilityInternal.h
, me di cuenta de que todas las versiones disponibles que están por encima del objetivo de Implementación están etiquetadas con la macro __AVAILABILITY_INTERNAL_WEAK_IMPORT
.
Por lo tanto, puedo generar advertencias redefiniendo esa macro:
#import <Availability.h>
#undef __AVAILABILITY_INTERNAL_WEAK_IMPORT
#define __AVAILABILITY_INTERNAL_WEAK_IMPORT /
__attribute__((weak_import,deprecated("API newer than Deployment Target.")))
Al colocar este código en el encabezado precompilado de un proyecto, cualquier uso de una API que pueda causar un bloqueo en la versión más baja compatible con iOS ahora genera una advertencia. Si guarda correctamente la llamada, puede desactivar la advertencia específicamente para esa llamada (modificada de la Guía de compatibilidad SDK de Apple):
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
if ([UIPrintInteractionController class]) {
// Create an instance of the class and use it.
}
#pragma GCC diagnostic warning "-Wdeprecated-declarations"
else {
// Alternate code path to follow when the
// class is not available.
}
El último Xcode no funcionó con otras respuestas. Esto funciona para mí (solo buscando problemas con UIKit).
La razón es que las versiones clang más nuevas tienen un atributo de disponibilidad incorporado.
#define TESTING_COMPILATION_TARGET
// only enable when trying to diagnose what APIs are being inappropriately used
#ifdef TESTING_COMPILATION_TARGET
#import <Availability.h>
#define __MYUNSUPPORTED __attribute((deprecated("API version unsupported")))
#define __MYUNSUPPORTED_IOS_NA __MYUNSUPPORTED
#define __MYUNSUPPORTED_IOS_2_0
#define __MYUNSUPPORTED_IOS_2_1
#define __MYUNSUPPORTED_IOS_2_2
#define __MYUNSUPPORTED_IOS_3_0
#define __MYUNSUPPORTED_IOS_3_1
#define __MYUNSUPPORTED_IOS_3_2
#define __MYUNSUPPORTED_IOS_4_0
#define __MYUNSUPPORTED_IOS_4_1
#define __MYUNSUPPORTED_IOS_4_2
#define __MYUNSUPPORTED_IOS_4_3
#define __MYUNSUPPORTED_IOS_5_0
#define __MYUNSUPPORTED_IOS_5_1
#define __MYUNSUPPORTED_IOS_6_0
#define __MYUNSUPPORTED_IOS_6_1
#define __MYUNSUPPORTED_IOS_7_0
#define __MYUNSUPPORTED_IOS_7_1 __MYUNSUPPORTED
#define __MYUNSUPPORTED_IOS_8_0 __MYUNSUPPORTED
#define __MYUNSUPPORTED_IOS_8_1 __MYUNSUPPORTED
#define __MYUNSUPPORTED_IOS_8_2 __MYUNSUPPORTED
#define __MYUNSUPPORTED_IOS_8_3 __MYUNSUPPORTED
#define __MYUNSUPPORTED_IOS_8_4 __MYUNSUPPORTED
#define __MYUNSUPPORTED_IOS_9_0 __MYUNSUPPORTED
#define __MYUNSUPPORTED_IOS_9_1 __MYUNSUPPORTED
#define __MYUNSUPPORTED_IOS_9_2 __MYUNSUPPORTED
#define __MYUNSUPPORTED_IOS_9_3 __MYUNSUPPORTED
#import <Foundation/Foundation.h>
#undef CF_AVAILABLE
#define CF_AVAILABLE(_mac, _ios) __MYUNSUPPORTED_IOS_##_ios
#undef NS_AVAILABLE
#define NS_AVAILABLE(_mac, _ios) __MYUNSUPPORTED_IOS_##_ios
#undef CF_AVAILABLE_IOS
#define CF_AVAILABLE_IOS(_ios) __MYUNSUPPORTED_IOS_##_ios
#undef NS_AVAILABLE_IOS
#define NS_AVAILABLE_IOS(_ios) __MYUNSUPPORTED_IOS_##_ios
#endif // testing
#import <UIKit/UIKit.h>
En OS X al menos, con clang / SDK reciente, ahora hay una -Wpartial-availability
(agréguela, por ejemplo, en "otras opciones de advertencia"). A continuación, puede definir las siguientes macros para encapsular código que maneja las pruebas de tiempo de ejecución si el método es soportado
#define START_IGNORE_PARTIAL _Pragma("clang diagnostic push") _Pragma("clang diagnostic ignored /"-Wpartial-availability/"")
#define END_IGNORE_PARTIAL _Pragma("clang diagnostic pop")
No he probado en iOS sin embargo.
Esto se basa en la respuesta de Ben S , pero incorpora soporte para GCC y LLVM-GCC. El atributo deprecated
de GCC no toma un argumento de mensaje como clang, por lo que al pasar uno se genera un error de compilación básicamente en cada archivo.
Coloque el siguiente código en la parte superior de su archivo ProjectName-Prefix.pch
para obtener una advertencia para cada uso de una API que puede no estar disponible en todas sus versiones específicas:
#import <Availability.h>
#undef __AVAILABILITY_INTERNAL_WEAK_IMPORT
#ifdef __clang__
#define __AVAILABILITY_INTERNAL_WEAK_IMPORT /
__attribute__((weak_import,deprecated("API newer than Deployment Target.")))
#else
#define __AVAILABILITY_INTERNAL_WEAK_IMPORT /
__attribute__((weak_import,deprecated))
#endif
Como dice Ben, si intencionalmente haces esto (tal vez buscando el selector en tiempo de ejecución), puedes ocultar la advertencia usando esta construcción:
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
- (void)conditionallyUseSomeAPI {
// Check for and use the appropriate API for this iOS version
}
#pragma GCC diagnostic warning "-Wdeprecated-declarations"
Lamentablemente, no se puede hacer esto dentro de una función, al menos en i686-apple-darwin10-llvm-gcc-4.2 (GCC) 4.2.1
.
No, no hay tal advertencia. Sin embargo, cuando usa una nueva API (ya que obviamente está escribiendo esto más tarde), simplemente verifique los documentos cuando estén disponibles.
Además, si admite 3.0 y está usando SDK nuevo para desarrollo, debe probar absolutamente en dispositivos reales con 3.0
Otra cosa que podría hacer es escribir su propia utilidad que analiza las macros de disponibilidad en los encabezados y luego le avisa si está llamando algo que no debería ser.
Sin embargo, debo reiterar que, si está orientando una versión anterior y utilizando el SDK más nuevo, debe verificar los documentos para ver cuándo estuvo disponible la API y realizar las pruebas adecuadas.
Para que esto funcione en XCode 5, también debe redefinir las macros NS_AVAILABLE y NS_DEPRECATED porque CFAvailability.h distingue entre compiladores que admiten la característica attribute_availability_with_message. Copie lo siguiente sobre la importación "MJGAvailability.h" en su encabezado precompilado para que esto funcione con el nuevo compilador LLVM de Apple:
#import <Availability.h>
#import <Foundation/NSObjCRuntime.h>
#undef CF_AVAILABLE
#undef CF_AVAILABLE_MAC
#undef CF_AVAILABLE_IOS
#undef CF_DEPRECATED
#undef CF_DEPRECATED_MAC
#undef CF_DEPRECATED_IOS
#undef CF_ENUM_AVAILABLE
#undef CF_ENUM_AVAILABLE_MAC
#undef CF_ENUM_AVAILABLE_IOS
#undef CF_ENUM_DEPRECATED
#undef CF_ENUM_DEPRECATED_MAC
#undef CF_ENUM_DEPRECATED_IOS
#undef NS_AVAILABLE
#undef NS_AVAILABLE_MAC
#undef NS_AVAILABLE_IOS
#undef NS_DEPRECATED
#undef NS_DEPRECATED_MAC
#undef NS_DEPRECATED_IOS
#undef NS_ENUM_AVAILABLE
#undef NS_ENUM_AVAILABLE_MAC
#undef NS_ENUM_AVAILABLE_IOS
#undef NS_ENUM_DEPRECATED
#undef NS_ENUM_DEPRECATED_MAC
#undef NS_ENUM_DEPRECATED_IOS
#undef NS_AVAILABLE_IPHONE
#undef NS_DEPRECATED_IPHONE
#undef NS_CLASS_AVAILABLE
#undef NS_CLASS_DEPRECATED
#undef NS_CLASS_AVAILABLE_IOS
#undef NS_CLASS_AVAILABLE_MAC
#undef NS_CLASS_DEPRECATED_MAC
#undef NS_CLASS_DEPRECATED_IOS
//CF macros redefinition
#define CF_AVAILABLE(_mac, _ios) __OSX_AVAILABLE_STARTING(__MAC_##_mac, __IPHONE_##_ios)
#define CF_AVAILABLE_MAC(_mac) __OSX_AVAILABLE_STARTING(__MAC_##_mac, __IPHONE_NA)
#define CF_AVAILABLE_IOS(_ios) __OSX_AVAILABLE_STARTING(__MAC_NA, __IPHONE_##_ios)
#define CF_DEPRECATED(_macIntro, _macDep, _iosIntro, _iosDep, ...) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_##_macIntro, __MAC_##_macDep, __IPHONE_##_iosIntro, __IPHONE_##_iosDep)
#define CF_DEPRECATED_MAC(_macIntro, _macDep, ...) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_##_macIntro, __MAC_##_macDep, __IPHONE_NA, __IPHONE_NA)
#define CF_DEPRECATED_IOS(_iosIntro, _iosDep, ...) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_NA, __MAC_NA, __IPHONE_##_iosIntro, __IPHONE_##_iosDep)
#define CF_ENUM_AVAILABLE(_mac, _ios) CF_AVAILABLE(_mac, _ios)
#define CF_ENUM_AVAILABLE_MAC(_mac) CF_AVAILABLE_MAC(_mac)
#define CF_ENUM_AVAILABLE_IOS(_ios) CF_AVAILABLE_IOS(_ios)
#define CF_ENUM_DEPRECATED(_macIntro, _macDep, _iosIntro, _iosDep, ...) CF_DEPRECATED(_macIntro, _macDep, _iosIntro, _iosDep, __VA_ARGS__)
#define CF_ENUM_DEPRECATED_MAC(_macIntro, _macDep, ...) CF_DEPRECATED_MAC(_macIntro, _macDep, __VA_ARGS__)
#define CF_ENUM_DEPRECATED_IOS(_iosIntro, _iosDep, ...) CF_DEPRECATED_IOS(_iosIntro, _iosDep, __VA_ARGS__)
//NS macros redefinition
#define NS_AVAILABLE(_mac, _ios) CF_AVAILABLE(_mac, _ios)
#define NS_AVAILABLE_MAC(_mac) CF_AVAILABLE_MAC(_mac)
#define NS_AVAILABLE_IOS(_ios) CF_AVAILABLE_IOS(_ios)
#define NS_DEPRECATED(_macIntro, _macDep, _iosIntro, _iosDep, ...) CF_DEPRECATED(_macIntro, _macDep, _iosIntro, _iosDep, __VA_ARGS__)
#define NS_DEPRECATED_MAC(_macIntro, _macDep, ...) CF_DEPRECATED_MAC(_macIntro, _macDep, __VA_ARGS__)
#define NS_DEPRECATED_IOS(_iosIntro, _iosDep, ...) CF_DEPRECATED_IOS(_iosIntro, _iosDep, __VA_ARGS__)
#define NS_ENUM_AVAILABLE(_mac, _ios) CF_ENUM_AVAILABLE(_mac, _ios)
#define NS_ENUM_AVAILABLE_MAC(_mac) CF_ENUM_AVAILABLE_MAC(_mac)
#define NS_ENUM_AVAILABLE_IOS(_ios) CF_ENUM_AVAILABLE_IOS(_ios)
#define NS_ENUM_DEPRECATED(_macIntro, _macDep, _iosIntro, _iosDep, ...) CF_ENUM_DEPRECATED(_macIntro, _macDep, _iosIntro, _iosDep, __VA_ARGS__)
#define NS_ENUM_DEPRECATED_MAC(_macIntro, _macDep, ...) CF_ENUM_DEPRECATED_MAC(_macIntro, _macDep, __VA_ARGS__)
#define NS_ENUM_DEPRECATED_IOS(_iosIntro, _iosDep, ...) CF_ENUM_DEPRECATED_IOS(_iosIntro, _iosDep, __VA_ARGS__)
#define NS_AVAILABLE_IPHONE(_ios) CF_AVAILABLE_IOS(_ios)
#define NS_DEPRECATED_IPHONE(_iosIntro, _iosDep) CF_DEPRECATED_IOS(_iosIntro, _iosDep)
#define NS_CLASS_AVAILABLE(_mac, _ios) __attribute__((visibility("default"))) NS_AVAILABLE(_mac, _ios)
#define NS_CLASS_DEPRECATED(_mac, _macDep, _ios, _iosDep, ...) __attribute__((visibility("default"))) NS_DEPRECATED(_mac, _macDep, _ios, _iosDep, __VA_ARGS__)
#define NS_CLASS_AVAILABLE_IOS(_ios) NS_CLASS_AVAILABLE(NA, _ios)
#define NS_CLASS_AVAILABLE_MAC(_mac) NS_CLASS_AVAILABLE(_mac, NA)
#define NS_CLASS_DEPRECATED_MAC(_macIntro, _macDep, ...) NS_CLASS_DEPRECATED(_macIntro, _macDep, NA, NA, __VA_ARGS__)
#define NS_CLASS_DEPRECATED_IOS(_iosIntro, _iosDep, ...) NS_CLASS_DEPRECATED(NA, NA, _iosIntro, _iosDep, __VA_ARGS__)
no está integrado en el conjunto de herramientas. Una opción para probar esto es simplemente crear un control en tiempo de ejecución que afirme (durante el desarrollo mientras se ejecuta en versiones más nuevas del sistema operativo).
assert([<CLASS> instancesRespondToSelector:@selector(potato)]);
luego solo agréguela a una de las rutinas de inicialización de su biblioteca.
también podría crear una secuencia de comandos que cuente la cantidad de advertencias emitidas para una traducción específica; si el conteo de la advertencia en cuestión cambia, entonces tiene que realizar las actualizaciones.