Vínculos débiles: compruebe si existe una clase y use esa clase

87

Estoy intentando crear una aplicación universal para iPhone, pero usa una clase definida solo en una versión más nueva del SDK. El marco existe en sistemas más antiguos, pero una clase definida en el marco no.

Sé que quiero usar algún tipo de enlace débil, pero cualquier documentación que pueda encontrar habla sobre verificaciones en tiempo de ejecución para la existencia de la función: ¿cómo verifico que existe una clase?

psicotik
fuente
Cual es la clase? Tal vez haya otras soluciones.
Marcelo Cantos

Respuestas:

164

TLDR

Actual:

  • Rápido :if #available(iOS 9, *)
  • Obj-C, iOS :if (@available(iOS 11.0, *))
  • Obj-C, OS X :if (NSClassFromString(@"UIAlertController"))

Legado:

  • Swift (versiones anteriores a la 2.0) :if objc_getClass("UIAlertController")
  • Obj-C, iOS (versiones anteriores a 4.2) :if (NSClassFromString(@"UIAlertController"))
  • Obj-C, iOS (versiones anteriores a la 11.0) :if ([UIAlertController class])

Swift 2+

Aunque históricamente se ha recomendado verificar las capacidades (o la existencia de clases) en lugar de versiones específicas del sistema operativo, esto no funciona bien en Swift 2.0 debido a la introducción de la verificación de disponibilidad .

Use esta forma en su lugar:

if #available(iOS 9, *) {
    // You can use UIStackView here with no errors
    let stackView = UIStackView(...)
} else {
    // Attempting to use UIStackView here will cause a compiler error
    let tableView = UITableView(...)
}

Nota: Si, en cambio, intenta utilizar objc_getClass(), obtendrá el siguiente error:

⛔️ 'UIAlertController' solo está disponible en iOS 8.0 o posterior.


Versiones anteriores de Swift

if objc_getClass("UIAlertController") != nil {
    let alert = UIAlertController(...)
} else {
    let alert = UIAlertView(...)
}

Tenga en cuenta que objc_getClass() es más confiable que NSClassFromString()oobjc_lookUpClass() .


Objective-C, iOS 4.2+

if ([SomeClass class]) {
    // class exists
    SomeClass *instance = [[SomeClass alloc] init];
} else {
    // class doesn't exist
}

Consulte la respuesta de code007 para obtener más detalles .


OS X o versiones anteriores de iOS

Class klass = NSClassFromString(@"SomeClass");
if (klass) {
    // class exists
    id instance = [[klass alloc] init];
} else {
    // class doesn't exist
}

Utilice NSClassFromString(). Si regresa nil, la clase no existe; de ​​lo contrario, devolverá el objeto de clase que se puede usar.

Esta es la forma recomendada según Apple en este documento :

[...] Su código probaría la existencia de [una] clase usando la NSClassFromString()cual devolverá un objeto de clase válido si [la] clase existe o nulo si no lo hace. Si la clase existe, su código puede usarla [...]

Sentido
fuente
Gracias. Solo para completar la respuesta para otros, una vez que haya detectado la clase, la cree usando la Classinstancia devuelta por NSClassFromString(asignarla a id) e invocar selectores en esa instancia.
psychotik
2
En particular, esta es la única forma en OSX, pero no la "mejor" forma en iOS (4.2+), aunque funcionará. Vea la respuesta de code007 para iOS específicamente.
Ben Mosher
¿Qué pasa si mi clase no es una clase real sino una estructura C?
Nathan H
Swift 4.1 viene con un nuevo cheque canImport. Propuesta
envío
69

Para proyectos nuevos que usan un SDK base de iOS 4.2 o posterior, existe este nuevo enfoque recomendado que consiste en usar el método de clase NSObject para verificar la disponibilidad de clases débilmente vinculadas en tiempo de ejecución. es decir

if ([UIPrintInteractionController class]) {
    // Create an instance of the class and use it.
} else {
    // Alternate code path to follow when the
    // class is not available.
}

fuente: https://developer.apple.com/library/content/documentation/DeveloperTools/Conceptual/cross_development/Using/using.html#//apple_ref/doc/uid/20002000-SW3

Este mecanismo utiliza la macro NS_CLASS_AVAILABLE, que está disponible para la mayoría de los marcos en iOS (tenga en cuenta que puede haber algunos marcos que aún no sean compatibles con NS_CLASS_AVAILABLE; consulte la nota de la versión de iOS). Es posible que también se necesite una configuración de configuración adicional que se puede leer en el enlace de documentación de Apple proporcionado anteriormente, sin embargo, la ventaja de este método es que obtiene una verificación de tipo estática.

código007
fuente
3
Un poco tarde para el juego, pero me encontré con este problema al intentar compilar el código que contenía UIAlertControllermientras seguía siendo compatible con iOS 7. La respuesta de code007 es correcta, pero la configuración adicional necesaria es vincular débilmente (establecer de Requireda Optional) UIKit en su proyecto (para esta situación, al menos).
Evan R