Weak Linking - periksa apakah ada kelas dan gunakan kelas itu

87

Saya mencoba membuat aplikasi iPhone universal, tetapi aplikasi tersebut menggunakan kelas yang hanya ditentukan dalam versi SDK yang lebih baru. Kerangka ada pada sistem yang lebih lama, tetapi kelas yang ditentukan dalam kerangka tidak.

Saya tahu saya ingin menggunakan semacam tautan yang lemah, tetapi setiap dokumentasi yang saya temukan berbicara tentang pemeriksaan runtime untuk keberadaan fungsi - bagaimana cara memeriksa bahwa kelas itu ada?

psikotik
sumber
Apa kelasnya? Mungkin ada solusi lain.
Marcelo Cantos

Jawaban:

164

TLDR

Arus:

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

Warisan:

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

Cepat 2+

Meskipun secara historis disarankan untuk memeriksa kemampuan (atau keberadaan kelas) daripada versi OS tertentu, ini tidak berfungsi dengan baik di Swift 2.0 karena diperkenalkannya pemeriksaan ketersediaan .

Gunakan cara ini sebagai gantinya:

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

Catatan: Jika Anda malah mencoba menggunakanobjc_getClass() , Anda akan mendapatkan error berikut:

⛔️ 'UIAlertController' hanya tersedia di iOS 8.0 atau yang lebih baru.


Versi Swift sebelumnya

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

Perhatikan bahwa objc_getClass() lebih dapat diandalkan daripada NSClassFromString()atauobjc_lookUpClass() .


Objective-C, iOS 4.2+

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

Lihat jawaban code007 untuk lebih jelasnya .


OS X atau iOS versi sebelumnya

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

Menggunakan NSClassFromString() . Jika dikembalikan nil, kelas tersebut tidak ada, jika tidak maka akan mengembalikan objek kelas yang dapat digunakan.

Ini adalah cara yang direkomendasikan menurut Apple dalam dokumen ini :

[...] Kode Anda akan menguji keberadaan kelas [a] NSClassFromString()yang akan mengembalikan objek kelas yang valid jika kelas [itu] ada atau nihil jika tidak ada. Jika kelas tersebut ada, kode Anda dapat menggunakannya [...]

Senseful
sumber
Terima kasih. Hanya untuk menyelesaikan jawaban untuk orang lain, setelah Anda mendeteksi kelas yang Anda buat menggunakan Classinstance yang dikembalikan oleh NSClassFromString(tetapkan ke id) dan panggil penyeleksi pada instance itu.
psikotik
2
Khususnya, ini adalah satu - satunya cara di OSX, tetapi bukan cara "terbaik" di iOS (4.2+), meskipun itu akan berhasil. Lihat jawaban code007 untuk iOS secara khusus.
Ben Mosher
Bagaimana jika kelas saya bukan kelas nyata melainkan C struct?
Nathan H
Swift 4.1 hadir dengan cek baru canImport. Proposal
dispatchMain
69

Untuk project baru yang menggunakan SDK dasar iOS 4.2 atau yang lebih baru, ada pendekatan baru yang direkomendasikan, yaitu menggunakan metode kelas NSObject untuk memeriksa ketersediaan kelas yang tertaut dengan lemah pada waktu proses. yaitu

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

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

Mekanisme ini menggunakan makro NS_CLASS_AVAILABLE, yang tersedia untuk sebagian besar framework di iOS (perhatikan mungkin ada beberapa framework yang belum mendukung NS_CLASS_AVAILABLE - periksa catatan rilis iOS untuk ini). Konfigurasi pengaturan tambahan mungkin juga diperlukan yang dapat dibaca di tautan dokumentasi Apple yang disediakan di atas, namun, keuntungan dari metode ini adalah Anda mendapatkan pemeriksaan tipe statis.

kode007
sumber
3
Agak terlambat untuk permainan, tetapi saya baru saja mengalami masalah ini ketika mencoba membangun kode yang berisi UIAlertControllersementara masih mendukung iOS 7. Jawaban code007 benar, tetapi konfigurasi tambahan yang diperlukan adalah untuk menautkan dengan lemah (diatur dari Requiredke Optional) UIKit dalam proyek Anda (setidaknya untuk situasi ini).
Evan R