Schwache Verknüpfung - Überprüfen Sie, ob eine Klasse vorhanden ist, und verwenden Sie diese Klasse

87

Ich versuche, eine universelle iPhone-App zu erstellen, aber sie verwendet eine Klasse, die nur in einer neueren Version des SDK definiert ist. Das Framework ist auf älteren Systemen vorhanden, eine im Framework definierte Klasse jedoch nicht.

Ich weiß, dass ich eine Art schwache Verknüpfung verwenden möchte, aber in jeder Dokumentation, die ich finden kann, finden sich Hinweise zu Laufzeitprüfungen für das Vorhandensein von Funktionen. Wie überprüfe ich, ob eine Klasse vorhanden ist?

Psychotik
quelle
Was ist die Klasse? Möglicherweise gibt es andere Problemumgehungen.
Marcelo Cantos

Antworten:

164

TLDR

Aktuell:

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

Erbe:

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

Swift 2+

Obwohl in der Vergangenheit empfohlen wurde, anstelle bestimmter Betriebssystemversionen nach Funktionen (oder Klassenexistenz) zu suchen, funktioniert dies in Swift 2.0 aufgrund der Einführung der Verfügbarkeitsprüfung nicht gut .

Verwenden Sie stattdessen diesen Weg:

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

Hinweis: Wenn Sie stattdessen versuchen, zu verwenden objc_getClass(), wird der folgende Fehler angezeigt:

⛔️ 'UIAlertController' ist nur unter iOS 8.0 oder neuer verfügbar.


Frühere Versionen von Swift

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

Beachten Sie, dass dies objc_getClass() zuverlässiger ist als NSClassFromString()oderobjc_lookUpClass() .


Objective-C, iOS 4.2+

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

Weitere Informationen finden Sie in der Antwort von code007 .


OS X oder frühere Versionen von iOS

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

Verwenden Sie NSClassFromString(). Wenn es zurückgibt nil, existiert die Klasse nicht, andernfalls wird das Klassenobjekt zurückgegeben, das verwendet werden kann.

Dies ist laut Apple in diesem Dokument der empfohlene Weg :

[...] Ihr Code würde die Existenz einer [a] -Klasse testen, NSClassFromString()die ein gültiges Klassenobjekt zurückgibt, wenn [die] Klasse existiert, oder null, wenn dies nicht der Fall ist. Wenn die Klasse existiert, kann Ihr Code sie verwenden [...]

Sinnvoll
quelle
Vielen Dank. Um die Antwort für andere zu vervollständigen, erstellen Sie die Klasse, sobald Sie sie erkannt haben, mit der von zurückgegebenen ClassInstanz NSClassFromString(weisen Sie sie zu id) und rufen Sie Selektoren für diese Instanz auf.
Psychotik
2
Dies ist insbesondere der einzige Weg unter OSX, aber nicht der "beste" Weg unter iOS (4.2+), obwohl es funktionieren wird. Siehe die Antwort von code007 speziell für iOS.
Ben Mosher
Was ist, wenn meine Klasse keine echte Klasse, sondern eine C-Struktur ist?
Nathan H
Swift 4.1 kommt mit neuem Check canImport. Vorschlag
dispatchMain
69

Für neue Projekte, die ein Basis-SDK von iOS 4.2 oder höher verwenden, gibt es diesen neuen empfohlenen Ansatz, bei dem die NSObject-Klassenmethode verwendet wird, um die Verfügbarkeit schwach verknüpfter Klassen zur Laufzeit zu überprüfen. dh

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

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

Dieser Mechanismus verwendet das Makro NS_CLASS_AVAILABLE, das für die meisten Frameworks in iOS verfügbar ist (möglicherweise gibt es einige Frameworks, die NS_CLASS_AVAILABLE noch nicht unterstützen - lesen Sie hierzu den Versionshinweis für iOS). Möglicherweise ist auch eine zusätzliche Einstellungskonfiguration erforderlich, die Sie über den oben angegebenen Link zur Dokumentation von Apple lesen können. Der Vorteil dieser Methode besteht jedoch darin, dass Sie eine statische Typprüfung erhalten.

code007
quelle
3
Ein wenig zu spät zum Spiel, aber ich bin gerade auf dieses Problem UIAlertControllergestoßen , als ich versucht habe, Code zu erstellen, der enthalten ist, während iOS 7 weiterhin unterstützt wird. Die Antwort von code007 ist korrekt, aber die zusätzliche Konfiguration besteht darin Required, OptionalUIKit in Ihrem Projekt schwach zu verknüpfen (von bis zu setzen ) (zumindest für diese Situation).
Evan R