Bạn đang tiếp cận nó theo cách sai: trong Swift, không giống như Objective-C, các lớp có các kiểu cụ thể và thậm chí có một hệ thống phân cấp thừa kế (nghĩa là, nếu lớp B
kế thừa từ A
, thì B.Type
cũng thừa hưởng từ A.Type
):
class A {}
class B: A {}
class C {}
// B inherits from A
let object: A = B()
// B.Type also inherits from A.Type
let type: A.Type = B.self
// Error: 'C' is not a subtype of 'A'
let type2: A.Type = C.self
Đó là lý do tại sao bạn không nên sử dụng AnyClass
, trừ khi bạn thực sự muốn cho phép bất kỳ lớp học nào . Trong trường hợp này, loại đúng sẽ là T.Type
bởi vì nó thể hiện liên kết giữa returningClass
tham số và tham số của bao đóng.
Trong thực tế, sử dụng nó thay vì AnyClass
cho phép trình biên dịch suy ra chính xác các kiểu trong lệnh gọi phương thức:
class func invokeService<T>(service: String, withParams params: Dictionary<String, String>, returningClass: T.Type, completionHandler handler: ((T) -> ())) {
// The compiler correctly infers that T is the class of the instances of returningClass
handler(returningClass())
}
Bây giờ có vấn đề về việc xây dựng một thể hiện T
để chuyển sang handler
: nếu bạn thử và chạy mã ngay bây giờ, trình biên dịch sẽ phàn nàn rằng T
không thể xây dựng được ()
. Và đúng như vậy: T
phải được ràng buộc rõ ràng để yêu cầu nó thực hiện một trình khởi tạo cụ thể.
Điều này có thể được thực hiện với một giao thức như sau:
protocol Initable {
init()
}
class CityInfo : NSObject, Initable {
var cityName: String?
var regionCode: String?
var regionName: String?
// Nothing to change here, CityInfo already implements init()
}
Sau đó, bạn chỉ phải thay đổi các ràng buộc chung invokeService
từ từ <T>
sang <T: Initable>
.
tiền boa
Nếu bạn gặp các lỗi lạ như "Không thể chuyển đổi biểu thức '()' sang loại 'Chuỗi'", việc di chuyển mọi đối số của lệnh gọi phương thức sang biến của chính nó là rất hữu ích. Nó giúp thu hẹp mã gây ra lỗi và phát hiện ra các vấn đề suy luận kiểu:
let service = "test"
let params = ["test" : "test"]
let returningClass = CityInfo.self
CastDAO.invokeService(service, withParams: params, returningClass: returningClass) { cityInfo in /*...*/
}
Bây giờ có hai khả năng: lỗi di chuyển đến một trong các biến (có nghĩa là phần sai ở đó) hoặc bạn nhận được một thông báo khó hiểu như "Không thể chuyển đổi loại biểu thức ()
thành loại ($T6) -> ($T6) -> $T5
".
Nguyên nhân của lỗi thứ hai là trình biên dịch không thể suy ra các loại của những gì bạn đã viết. Trong trường hợp này, vấn đề là T
chỉ được sử dụng trong tham số của bao đóng và bao đóng mà bạn đã chuyển không chỉ ra bất kỳ loại cụ thể nào để trình biên dịch không biết loại nào sẽ suy ra. Bằng cách thay đổi kiểu returningClass
bao gồm, T
bạn cung cấp cho trình biên dịch một cách để xác định tham số chung.
CastDAO.invokeService("test", withParams: ["test" : "test"]) { (ci:CityInfo) in }