2016-01-25 14 views
8

API-C Mục tiêu làm rất nhiều xây dựng đối tượng sử dụng phương pháp lớp:Swift lỗi: Phương pháp lớp "someMethod() là không có sẵn, Class xây dựng sử dụng đối tượng()"

+ (NSDate *)date; 
+ (NSURL *)urlWithString:(NSString *)string; 
+ (instancetype)layerWithSession:(AVCaptureSession *)session 

Đôi khi tôi thậm chí nhìn thấy những xuất hiện trong hướng dẫn Swift cũ như các phương pháp lớp học, nhưng khi tôi cố gắng gọi cho họ, tôi nhận được lỗi biên dịch như thế này:

date() is unavailable, use object construction NSDate()

urlWithString() is unavailable, use object construction NSURL(string:)

layerWithSession() is unavailable, use object construction AVCaptureVideoPreviewLayer(session:)

(tôi thấy tờ khai như convenience init(URL url: NSURL) trong các tài liệu, và trong giao diện Swift tạo Xcode cho lớp SDK, nhưng họ phải làm gì với điều này?)

Điều này thậm chí xảy ra khi tôi tuyên bố phương pháp lớp trong mã ObjC của riêng tôi, như một mẫu singleton:

@interface MyManager: NSObject 
+ (instancetype)manager; 
@end 

Khi tôi cố gắng sử dụng chúng từ Swift, tôi nhận được cùng một loại lỗi:

let manager = MyManager.manager() 
// error: 'manager()' is unavailable, use object construction 'MyManager()' 

Điều gì mang lại? Làm cách nào để sửa các lỗi này?

+0

Tôi thấy rất nhiều câu hỏi với vấn đề này là nguyên nhân gốc rễ, nhưng nơi thảo luận và câu trả lời quá gắn với các trường hợp cụ thể được đánh dấu là trùng lặp với nhau. Dưới đây là một nỗ lực để khái quát vấn đề - với những người khác có quyền đóng các bản sao, bạn có thể tự do lừa đảo vấn đề này nếu bạn nghĩ rằng nó giải quyết vấn đề đủ tốt. (Tôi sẽ kiêng bản thân mình để cho cộng đồng trở thành thẩm phán về sự xứng đáng của hai người.) – rickster

Trả lời

13

Swift không sử dụng mẫu alloc/init khởi tạo từ Objective-C và thay thế bằng cú pháp xây dựng tương tự như được thấy trong Java hoặc C++.

Khi nào, trong một giao diện lớp Swift (hoặc trong tài liệu hướng dẫn của Apple), bạn sẽ thấy một cái gì đó như:

class Thing { 
    init(number: Int) 
} 

Điều đó có nghĩa bạn có thể tạo một Thing bằng cách gọi nó như sau "cú pháp xây dựng":

let thingTwo = Thing(number: 2) 

nó không quan trọng nếu đó là một class hoặc một struct, hoặc nếu bạn thấy những thứ khác như convenience hoặc required hoặc public trước init ... đó là cách bạn sử dụng nó. (Tất nhiên, thay Thing với loại mà bạn đang sử dụng, number với bất kỳ nhãn tham số (s) trong initializer thực tế mà bạn muốn, và 2 với một giá trị thích hợp cho tham số đó.)


Khi lớp học ban đầu được định nghĩa trong ObjC được nhập vào Swift, trình biên dịch Swift thực hiện một loạt các thứ để làm cho các API đó trông giống và cảm thấy giống mã Swift gốc hơn. Một trong những điều đó là thay thế mẫu chung của việc sử dụng các phương thức lớp như các hàm tạo thuận tiện với các trình khởi tạo thực.

Tất cả các ví dụ từ câu hỏi:

+ (NSDate *)date; 
+ (NSURL *)urlWithString:(NSString *)string; 
+ (instancetype)layerWithSession:(AVCaptureSession *)session; 

... là những nhà xây dựng thuận tiện như vậy. Điều đó có nghĩa họ nhập vào Swift không phải là phương pháp lớp (NSDate.date(), NSURL.urlWithString(), vv), nhưng như initializers:

class NSDate { 
    init() 
} 
class NSURL { 
    init?(string: String) 
} 
class AVCaptureVideoPreviewLayer { 
    init!(session: AVCaptureSession) 
} 

...và bạn gọi cho họ với cú pháp xây dựng đối tượng:

let date = NSDate() 
let url = NSURL(string: "http://apple.com") 
let layer = AVCaptureVideoPreviewLayer(session: mySession) 

Trong thực tế, khi bạn nhìn thấy thông báo lỗi về "không có sẵn, sử dụng đối tượng xây dựng", Xcode khá nhiều luôn cung cấp một tự động sửa chữa-nó để chèn đúng cú pháp.


Tính năng này hoạt động như thế nào? Swift tìm kiếm các "tên cơ sở" có thể có của một lớp và kết hợp giữa các tên đó và tên phương thức/tên phương thức khởi tạo lớp. (Nó cũng kiểm tra các phương pháp lớp đó trở lại một thể hiện của lớp đó, initializers bắt đầu bằng "init ...", vv)

Ví dụ, Swift thấy sau như một constructor tiện theo dõi:

@interface PDQMediaManager: NSObject 
+ (PDQMediaManager *)manager; 
@end 

Điều này có thể là vấn đề nếu bạn (hoặc nhà phát triển ban đầu của PDQMediaManager) có ý định cho lớp đó hoạt động như một singleton và phương thức +manager để trả lại bản sao đơn - nếu nhà phát triển Swift viết PDQMediaManager(), xây dựng một thể hiện mới thay vì truy xuất singleton.

Trong trường hợp này, một trong những giải pháp tốt nhất là đổi tên phương pháp singleton:

+ (PDQMediaManager *)defaultManager; 

này sẽ không được coi là một nhà xây dựng thuận tiện, khách hàng nên Swift có thể gọi PDQMediaManager.defaultManager() để có được những ví dụ singleton.

Bạn cũng có thể sử dụng NS_SWIFT_NAME để đổi tên các phương pháp singleton chỉ dành cho khách hàng Swift:

+ (PDQMediaManager *)manager NS_SWIFT_NAME(defaultManager()); 

Nếu bạn không kiểm soát các codebase với phương pháp vi phạm, đặt cược tốt nhất của bạn có lẽ là để viết ObjC của riêng bạn để chuyển tiếp sang phương thức singleton và hiển thị mã Swift của bạn thông qua một tiêu đề bắc cầu.

Các vấn đề liên quan