Liên kết yếu - kiểm tra xem một lớp có tồn tại hay không và sử dụng lớp đó


87

Tôi đang cố gắng tạo một ứng dụng iPhone toàn cầu, nhưng nó sử dụng một lớp chỉ được xác định trong phiên bản SDK mới hơn. Khuôn khổ tồn tại trên các hệ thống cũ hơn, nhưng một lớp được định nghĩa trong khuôn khổ thì không.

Tôi biết tôi muốn sử dụng một số loại liên kết yếu, nhưng bất kỳ tài liệu nào tôi có thể tìm thấy đều nói về kiểm tra thời gian chạy cho sự tồn tại của hàm - làm cách nào để kiểm tra xem một lớp có tồn tại không?


Lớp học là gì? Có thể có những cách giải quyết khác.
Marcelo Cantos

Câu trả lời:


164

TLDR

Hiện hành:

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

Di sản:

  • Swift (các phiên bản trước 2.0) :if objc_getClass("UIAlertController")
  • Obj-C, iOS (phiên bản trước 4.2) :if (NSClassFromString(@"UIAlertController"))
  • Obj-C, iOS (phiên bản trước 11.0) :if ([UIAlertController class])

Swift 2+

Mặc dù trước đây người ta khuyên bạn nên kiểm tra các khả năng (hoặc sự tồn tại của lớp) hơn là các phiên bản hệ điều hành cụ thể, nhưng điều này không hoạt động tốt trong Swift 2.0 vì sự ra đời của tính năng kiểm tra tính khả dụng .

Sử dụng cách này để thay thế:

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

Lưu ý: Nếu bạn cố gắng sử dụng thay vào đó objc_getClass(), bạn sẽ gặp lỗi sau:

⛔️ 'UIAlertController' chỉ khả dụng trên iOS 8.0 hoặc mới hơn.


Các phiên bản trước của Swift

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

Lưu ý rằng objc_getClass() đáng tin cậy hơn NSClassFromString()hoặcobjc_lookUpClass() .


Objective-C, iOS 4.2+

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

Xem câu trả lời của mã007 để biết thêm chi tiết .


OS X hoặc các phiên bản trước của iOS

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

Sử dụng NSClassFromString(). Nếu nó trả về nil, lớp không tồn tại, nếu không nó sẽ trả về đối tượng lớp có thể được sử dụng.

Đây là cách được Apple khuyến nghị trong tài liệu này :

[...] Mã của bạn sẽ kiểm tra sự tồn tại của [a] lớp bằng cách sử dụng NSClassFromString()nó sẽ trả về một đối tượng lớp hợp lệ nếu [lớp] tồn tại hoặc nil nếu nó không. Nếu lớp tồn tại, mã của bạn có thể sử dụng nó [...]


Cảm ơn. Chỉ để hoàn thành câu trả lời cho những người khác, khi bạn đã phát hiện ra lớp, bạn tạo nó bằng cách sử dụng Classthể hiện được trả về bởi NSClassFromString(gán nó cho id) và gọi các bộ chọn trên thể hiện đó.
Psychotik

2
Đáng chú ý, đây là cách duy nhất trên OSX, nhưng không phải là cách "tốt nhất" trên iOS (4.2+), mặc dù nó sẽ hoạt động. Xem câu trả lời của code007 dành cho iOS cụ thể.
Ben Mosher

Điều gì sẽ xảy ra nếu lớp của tôi không phải là một lớp thực mà là một cấu trúc C?
Nathan H

Swift 4.1 sắp ra mắt với séc mới canImport. Đề xuất
văn Chính

69

Đối với các dự án mới sử dụng SDK cơ sở của iOS 4.2 trở lên, có một cách tiếp cận được đề xuất mới này là sử dụng phương pháp lớp NSObject để kiểm tra tính khả dụng của các lớp được liên kết yếu tại thời điểm chạy. I E

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

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

Cơ chế này sử dụng macro NS_CLASS_AVAILABLE, có sẵn cho hầu hết các khung trong iOS (lưu ý rằng có thể có một số khung chưa hỗ trợ NS_CLASS_AVAILABLE - hãy kiểm tra ghi chú phát hành iOS để biết điều này). Cũng có thể cần cấu hình cài đặt bổ sung mà bạn có thể đọc trong liên kết tài liệu của Apple được cung cấp ở trên, tuy nhiên, ưu điểm của phương pháp này là bạn có thể kiểm tra kiểu tĩnh.


3
Trò chơi hơi muộn, nhưng tôi chỉ gặp sự cố này khi cố gắng xây dựng mã chứa UIAlertControllertrong khi vẫn hỗ trợ iOS 7. Câu trả lời của code007 là đúng, nhưng cấu hình bổ sung cần thiết là liên kết yếu (đặt từ Requiredthành Optional) UIKit trong dự án của bạn (ít nhất là đối với tình huống này).
Evan R
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.