我有一个泛型函数,它调用Web服务并将JSON响应序列化回对象。
class func invokeService<T>(service: String, withParams params: Dictionary<String, String>, returningClass: AnyClass, completionHandler handler: ((T) -> ())) {
/* Construct the URL, call the service and parse the response */
}
我想要完成的是相当于这个Java代码
public <T> T invokeService(final String serviceURLSuffix, final Map<String, String> params,
final Class<T> classTypeToReturn) {
}
AnyClass
作为参数类型
正确的事情吗?MyObject.self
作为returnsClass值传递,但是我得到一个编译错误“无法将表达式的类型'()转换为'String'” CastDAO.invokeService("test", withParams: ["test" : "test"], returningClass: CityInfo.self) { cityInfo in /*...*/
}
修改:
我尝试使用object_getClass
,如holex所述,但现在我得到了:
错误:“类型'CityInfo.Type'不符合协议'AnyObject'”
要遵守协议需要做些什么?
class CityInfo : NSObject {
var cityName: String?
var regionCode: String?
var regionName: String?
}
答案 0 :(得分:127)
您正在以错误的方式接近它:在Swift中,与Objective-C不同,类具有特定类型,甚至具有继承层次结构(即,如果类B
继承自A
,则B.Type
也继承自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
这就是为什么你不应该使用AnyClass
,除非你真的想要允许任何类。在这种情况下,正确的类型将是T.Type
,因为它表示returningClass
参数和闭包参数之间的链接。
实际上,使用它代替AnyClass
允许编译器正确推断方法调用中的类型:
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())
}
现在存在构建T
实例以传递给handler
的问题:如果您现在尝试运行代码,编译器会抱怨T
无法构造{ {1}}。理所当然地:()
必须明确约束,要求它实现特定的初始化程序。
这可以通过以下协议来完成:
T
然后,您只需将protocol Initable {
init()
}
class CityInfo : NSObject, Initable {
var cityName: String?
var regionCode: String?
var regionName: String?
// Nothing to change here, CityInfo already implements init()
}
的一般约束从invokeService
更改为<T>
。
如果您遇到奇怪的错误,例如“无法将表达式的类型'(')转换为'String'”,则将方法调用的每个参数移动到其自己的变量通常很有用。它有助于缩小导致错误的代码并发现类型推断问题:
<T: Initable>
现在有两种可能性:错误移动到其中一个变量(这意味着错误的部分存在)或者你得到一个神秘的消息,例如“无法将表达式的类型let service = "test"
let params = ["test" : "test"]
let returningClass = CityInfo.self
CastDAO.invokeService(service, withParams: params, returningClass: returningClass) { cityInfo in /*...*/
}
转换为类型{{1 }}”。
后一个错误的原因是编译器无法推断您编写的内容的类型。在这种情况下,问题是()
仅用于闭包的参数,并且您传递的闭包不指示任何特定类型,因此编译器不知道要推断的类型。通过将($T6) -> ($T6) -> $T5
的类型更改为包含T
,您可以为编译器提供一种确定泛型参数的方法。
答案 1 :(得分:32)
你可以通过这种方式获得AnyObject
课程:
let myClass: AnyClass = type(of: self)
let myClass: AnyClass = object_getClass(self)
如果您愿意,可以稍后将其作为参数传递。
答案 2 :(得分:2)
在swift5中我有一个类似的用例:
class PlistUtils {
static let shared = PlistUtils()
// write data
func saveItem<T: Encodable>(url: URL, value: T) -> Bool{
let encoder = PropertyListEncoder()
do {
let data = try encoder.encode(value)
try data.write(to: url)
return true
}catch {
print("encode error: \(error)")
return false
}
}
// read data
func loadItem<T: Decodable>(url: URL, type: T.Type) -> Any?{
if let data = try? Data(contentsOf: url) {
let decoder = PropertyListDecoder()
do {
let result = try decoder.decode(type, from: data)
return result
}catch{
print("items decode failed ")
return nil
}
}
return nil
}
}
答案 3 :(得分:0)
使用obj-getclass
:
CastDAO.invokeService("test", withParams: ["test" : "test"], returningClass: obj-getclass(self)) { cityInfo in /*...*/
}
假设self是城市信息对象。
答案 4 :(得分:0)
情况不完全相同,但是我遇到了类似的问题。最终帮助我的是:
func myFunction(_ myType: AnyClass)
{
switch type
{
case is MyCustomClass.Type:
//...
break
case is MyCustomClassTwo.Type:
//...
break
default: break
}
}
然后您可以在上述类的实例中调用它,如下所示:
myFunction(type(of: self))
希望这对处于我相同情况的人有所帮助。
答案 5 :(得分:0)
我最近遇到了这种情况,它在寻找一种使UINavigationController对子视图按钮以外的所有东西不可见的方法。我将其放在自定义导航控制器中:
// MARK:- UINavigationBar Override
private extension UINavigationBar {
override open func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
// Make the navigation bar ignore interactions unless with a subview button
return self.point(inside: point, with: event, type: UIButton.self)
}
}
// MARK:- Button finding hit test
private extension UIView {
func point<T: UIView>(inside point: CGPoint, with event: UIEvent?, type: T.Type) -> Bool {
guard self.bounds.contains(point) else { return false }
if subviews.contains(where: { $0.point(inside: convert(point, to: $0), with: event, type: type) }) {
return true
}
return self is T
}
}
别忘了使用边界而不是帧,因为在调用之前转换了点。