Collegamento debole: controlla se esiste una classe e usa quella classe


87

Sto cercando di creare un'app universale per iPhone, ma utilizza una classe definita solo in una versione più recente dell'SDK. Il framework esiste su sistemi più vecchi, ma una classe definita nel framework no.

So di voler utilizzare un qualche tipo di collegamento debole, ma qualsiasi documentazione che riesco a trovare parla di controlli di runtime per l'esistenza di funzioni - come posso verificare che una classe esista?


Qual è la classe? Forse ci sono altre soluzioni alternative.
Marcelo Cantos

Risposte:


164

TLDR

Attuale:

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

Legacy:

  • Swift (versioni precedenti alla 2.0) :if objc_getClass("UIAlertController")
  • Obj-C, iOS (versioni precedenti alla 4.2) :if (NSClassFromString(@"UIAlertController"))
  • Obj-C, iOS (versioni precedenti alla 11.0) :if ([UIAlertController class])

Swift 2+

Sebbene storicamente sia stato consigliato di verificare le capacità (o l'esistenza di una classe) piuttosto che versioni specifiche del sistema operativo, questo non funziona bene in Swift 2.0 a causa dell'introduzione del controllo della disponibilità .

Usa invece in questo modo:

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: se si tenta invece di utilizzare objc_getClass(), verrà visualizzato il seguente errore:

⛔️ 'UIAlertController' è disponibile solo su iOS 8.0 o versioni successive.


Versioni precedenti di Swift

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

Nota che objc_getClass() è più affidabile di NSClassFromString()oobjc_lookUpClass() .


Objective-C, iOS 4.2+

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

Vedi la risposta di code007 per maggiori dettagli .


OS X o versioni precedenti di iOS

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

Usa NSClassFromString(). Se ritorna nil, la classe non esiste, altrimenti restituirà l'oggetto classe che può essere utilizzato.

Questo è il modo consigliato secondo Apple in questo documento :

[...] Il tuo codice testerebbe l'esistenza di [una] classe usando NSClassFromString()che restituirà un oggetto di classe valido se [la] classe esiste o nulla se non lo fa. Se la classe esiste, il tuo codice può usarla [...]


Grazie. Solo per completare la risposta per gli altri, una volta rilevata la classe, la crei utilizzando l' Classistanza restituita da NSClassFromString(assegnala a id) e invoca i selettori su quell'istanza.
psychotik

2
In particolare, questo è l' unico modo su OSX, ma non il modo "migliore" su iOS (4.2+), anche se funzionerà. Vedi la risposta di code007 per iOS in particolare.
Ben Mosher

E se la mia classe non fosse una vera classe ma piuttosto una struttura C?
Nathan H

Swift 4.1 è in arrivo con un nuovo controllo canImport. Proposta
invio

69

Per i nuovi progetti che utilizzano un SDK di base di iOS 4.2 o versioni successive, esiste questo nuovo approccio consigliato che consiste nell'utilizzare il metodo della classe NSObject per verificare la disponibilità di classi debolmente collegate in fase di esecuzione. cioè

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

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

Questo meccanismo utilizza la macro NS_CLASS_AVAILABLE, che è disponibile per la maggior parte dei framework in iOS (nota che potrebbero esserci alcuni framework che non supportano ancora NS_CLASS_AVAILABLE - controlla la nota di rilascio di iOS per questo). Potrebbe essere necessaria anche una configurazione di impostazioni aggiuntiva che può essere letta nel collegamento alla documentazione di Apple fornito sopra, tuttavia, il vantaggio di questo metodo è che si ottiene il controllo del tipo statico.


3
Un po 'tardi per il gioco, ma mi sono imbattuto in questo problema durante il tentativo di creare codice che conteneva UIAlertControllerpur supportando iOS 7. La risposta di code007 è corretta, ma la configurazione aggiuntiva necessaria è quella di collegare debolmente (impostato da Requireda Optional) UIKit nel progetto (per questa situazione, almeno).
Evan R,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.