Слабке зв’язування - перевірте, чи існує клас, і використовуйте цей клас


87

Я намагаюся створити універсальний додаток для iPhone, але він використовує клас, визначений лише в новішій версії SDK. Фреймворк існує у старих системах, але клас, визначений у фреймворку, цього не робить.

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


Який клас? Можливо, є й інші обхідні шляхи.
Марсело Кантос,

Відповіді:


164

TLDR

Струм:

  • Стрімкий :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])

Свіфт 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()який поверне дійсний об’єкт класу, якщо клас [the] існує, або нуль, якщо цього не відбувається. Якщо клас існує, ваш код може використовувати його [...]


Дякую. Щоб заповнити відповідь для інших, як тільки ви виявите клас, ви створюєте його за допомогою Classекземпляра, який повертається NSClassFromString(призначити id), і викликаєте селектори на цьому екземплярі.
psychotik

2
Примітно, що це єдиний спосіб на OSX, але не найкращий спосіб на iOS (4.2+), хоча він буде працювати. Див. Відповідь code007 для iOS конкретно.
Ben Mosher

Що робити, якщо мій клас - це не справжній клас, а скоріше структура C?
Nathan H

Swift 4.1 виходить з новою перевіркою canImport. Пропозиція
відправка,

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, однак перевага цього методу полягає в тому, що ви отримуєте статичну перевірку типу.


3
Трохи запізнився в грі, але я просто зіткнувся з цією проблемою при спробі побудувати код, який містив UIAlertController, підтримуючи iOS 7. Відповідь code007 правильна, але потрібна додаткова конфігурація, щоб слабо зв’язати (встановити з ) Requiredдо OptionalUIKit у вашому проекті (принаймні для цієї ситуації).
Еван Р,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.