Слабое связывание - проверьте, существует ли класс, и используйте этот класс

87

Я пытаюсь создать универсальное приложение для iPhone, но оно использует класс, определенный только в более новой версии SDK. Фреймворк существует в более старых системах, но класс, определенный в фреймворке, не существует.

Я знаю, что хочу использовать какую-то слабую связь, но в любой документации, которую я могу найти, говорится о проверках существования функции во время выполнения - как мне проверить, существует ли класс?

психотик
источник
Что за класс? Возможно, есть другие обходные пути.
Марсело Кантос,

Ответы:

164

TL; DR

Ток:

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

Наследие:

  • Swift (версии до 2.0) :if objc_getClass("UIAlertController")
  • Obj-C, iOS (версии до 4.2) :if (NSClassFromString(@"UIAlertController"))
  • Obj-C, iOS (версии до 11.0) :if ([UIAlertController class])

Swift 2+

Хотя исторически рекомендуется проверять возможности (или наличие классов), а не конкретные версии ОС, в Swift 2.0 это не работает из-за введения проверки доступности .

Вместо этого используйте этот способ:

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

Примечание. Если вы вместо этого попытаетесь использовать objc_getClass(), вы получите следующую ошибку:

⛔️ «UIAlertController» доступен только в iOS 8.0 или новее.


Предыдущие версии Swift

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

Обратите внимание, что objc_getClass() это более надежно, чем NSClassFromString()илиobjc_lookUpClass() .


Objective-C, iOS 4.2+

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

См . Ответ code007 для более подробной информации .


OS X или предыдущие версии iOS

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

Используйте NSClassFromString(). Если он возвращается nil, класс не существует, иначе он вернет объект класса, который можно использовать.

Это рекомендуемый способ, согласно Apple в этом документе, это :

[...] Ваш код будет проверять существование [a] класса, используя NSClassFromString()который будет возвращать действительный объект класса, если [этот] класс существует, или ноль, если его нет. Если класс действительно существует, ваш код может его использовать [...]

Чувственный
источник
Спасибо. Просто чтобы завершить ответ для других, как только вы обнаружите класс, который вы создаете, используя Classэкземпляр, возвращенный NSClassFromString(назначьте его id), и вызовите селекторы для этого экземпляра.
psychotik
2
Примечательно, что это единственный способ для OSX, но не «лучший» способ для iOS (4.2+), хотя он будет работать. См. Ответ code007 специально для iOS.
Бен Мошер
Что, если мой класс - это не реальный класс, а структура C?
Nathan H
Swift 4.1 выходит с новой проверкой canImport. Предложение
dispatchMain
69

Для новых проектов, использующих базовый SDK iOS 4.2 или новее, есть этот новый рекомендуемый подход, который заключается в использовании метода класса NSObject для проверки доступности слабо связанных классов во время выполнения. т.е.

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

источник: https://developer.apple.com/library/content/documentation/DeveloperTools/Conceptual/cross_development/Using/using.html#//apple_ref/doc/uid/20002000-SW3

Этот механизм использует макрос NS_CLASS_AVAILABLE, который доступен для большинства фреймворков в iOS (обратите внимание, что могут быть некоторые фреймворки, которые еще не поддерживают NS_CLASS_AVAILABLE - проверьте примечания к выпуску iOS для этого). Также может потребоваться дополнительная настройка параметров, которую можно прочитать в приведенной выше ссылке на документацию Apple, однако преимущество этого метода заключается в том, что вы получаете проверку статического типа.

code007
источник
3
Немного поздно в игре, но я столкнулся с этой проблемой, когда пытался создать код, который содержал, UIAlertControllerно все еще поддерживал iOS 7. Ответ code007 правильный, но требуется дополнительная конфигурация, чтобы слабо связать (установить с Requiredпо Optional) UIKit в вашем проекте (по крайней мере, для этой ситуации).
Evan R