Lien faible - vérifiez si une classe existe et utilisez cette classe

87

J'essaie de créer une application iPhone universelle, mais elle utilise une classe définie uniquement dans une version plus récente du SDK. Le framework existe sur des systèmes plus anciens, mais une classe définie dans le framework ne l'est pas.

Je sais que je veux utiliser une sorte de lien faible, mais toute documentation que je peux trouver parle des vérifications à l'exécution de l'existence des fonctions - comment vérifier qu'une classe existe?

psychotik
la source
Quelle est la classe? Il existe peut-être d'autres solutions de contournement.
Marcelo Cantos

Réponses:

164

TLDR

Actuel:

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

Héritage:

  • Swift (versions antérieures à 2.0) :if objc_getClass("UIAlertController")
  • Obj-C, iOS (versions antérieures à 4.2) :if (NSClassFromString(@"UIAlertController"))
  • Obj-C, iOS (versions antérieures à 11.0) :if ([UIAlertController class])

Swift 2+

Bien qu'historiquement, il ait été recommandé de vérifier les capacités (ou l'existence de classe) plutôt que des versions spécifiques du système d'exploitation, cela ne fonctionne pas bien dans Swift 2.0 en raison de l'introduction de la vérification de disponibilité .

Utilisez plutôt cette méthode:

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(...)
}

Remarque: si vous essayez d'utiliser à la place objc_getClass(), vous obtiendrez l'erreur suivante:

⛔️ 'UIAlertController' n'est disponible que sur iOS 8.0 ou plus récent.


Versions précédentes de Swift

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

Notez que objc_getClass() c'est plus fiable que NSClassFromString()ouobjc_lookUpClass() .


Objective-C, iOS 4.2+

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

Voir la réponse de code007 pour plus de détails .


OS X ou versions précédentes d'iOS

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

Utilisez NSClassFromString(). S'il retourne nil, la classe n'existe pas, sinon elle retournera l'objet de classe qui peut être utilisé.

C'est la méthode recommandée selon Apple dans ce document :

[...] Votre code testerait l'existence d'une [a] classe en utilisant NSClassFromString()qui retournera un objet de classe valide si [la] classe existe ou nul si ce n'est pas le cas. Si la classe existe, votre code peut l'utiliser [...]

Sensé
la source
Merci. Juste pour compléter la réponse pour les autres, une fois que vous avez détecté la classe, vous la créez à l'aide de l' Classinstance renvoyée par NSClassFromString(affectez-la à id) et appelez des sélecteurs sur cette instance.
psychotik
2
Notamment, c'est le seul moyen sur OSX, mais pas le "meilleur" moyen sur iOS (4.2+), même si cela fonctionnera. Voir la réponse de code007 pour iOS en particulier.
Ben Mosher
Et si ma classe n'est pas une vraie classe mais plutôt une structure C?
Nathan H
Swift 4.1 vient avec un nouveau chèque canImport. Proposition
expéditionMain
69

Pour les nouveaux projets qui utilisent un SDK de base d'iOS 4.2 ou version ultérieure, il existe cette nouvelle approche recommandée qui consiste à utiliser la méthode de classe NSObject pour vérifier la disponibilité des classes faiblement liées au moment de l'exécution. c'est à dire

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

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

Ce mécanisme utilise la macro NS_CLASS_AVAILABLE, qui est disponible pour la plupart des frameworks sous iOS (notez qu'il peut y avoir un framework qui ne prend pas encore en charge NS_CLASS_AVAILABLE - consultez la note de publication iOS pour cela). Une configuration de paramètres supplémentaire peut également être nécessaire, qui peut être lue dans le lien de documentation d'Apple fourni ci-dessus, cependant, l'avantage de cette méthode est que vous obtenez une vérification de type statique.

code007
la source
3
Un peu tard dans le jeu, mais je viens de rencontrer ce problème en essayant de créer du code qui contenait UIAlertControllertout en prenant en charge iOS 7. La réponse de code007 est correcte, mais la configuration supplémentaire nécessaire est de lier faiblement (défini de Requiredà Optional) UIKit dans votre projet (pour cette situation, au moins).
Evan R