swift - 如何将类类型作为函数参数传递

标签 swift generics type-inference

我有一个调用 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 作为 returningClass 值传递,但出现编译错误“无法将表达式的类型‘()’转换为‘String’类型'"
CastDAO.invokeService("test", withParams: ["test" : "test"], returningClass: CityInfo.self) { cityInfo in /*...*/

}

编辑:

我尝试使用 object_getClass,如 holex 所述,但现在我得到:

error: "Type 'CityInfo.Type' does not conform to protocol 'AnyObject'"

需要做什么才能符合协议(protocol)?

class CityInfo : NSObject {

    var cityName: String?
    var regionCode: String?
    var regionName: String?
}

最佳答案

您以错误的方式处理它:在 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不能用 () 构造.理所当然:T必须明确限制要求它实现特定的初始化器。

这可以通过如下协议(protocol)来完成:

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><T: Initable> .

提示

如果您遇到奇怪的错误,例如“无法将表达式的类型 '()' 转换为类型 'String'”,将方​​法调用的每个参数移动到其自己的变量通常很有用。它有助于缩小导致错误的代码范围并发现类型推断问题:

let service = "test"
let params = ["test" : "test"]
let returningClass = CityInfo.self

CastDAO.invokeService(service, withParams: params, returningClass: returningClass) { cityInfo in /*...*/

}

现在有两种可能性:错误移动到其中一个变量(这意味着那里有错误的部分),或者您收到一条神秘消息,如“无法将表达式的类型 () 转换为类型 ($T6) -> ($T6) -> $T5”。

后一个错误的原因是编译器无法推断你所写内容的类型。在这种情况下,问题是 T仅在闭包的参数中使用,您传递的闭包不指示任何特定类型,因此编译器不知道要推断什么类型。通过更改 returningClass 的类型包括 T您为编译器提供了一种确定泛型参数的方法。

关于swift - 如何将类类型作为函数参数传递,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24308975/

相关文章:

iOS:UITabBarController 下面有广告

c# - 受约束泛型类型参数的继承

c# - 通用方法设置基类属性的值

scala - 如何从投影类型推断正确的类型参数?

haskell - GHC 类型推断问题

rust - 为什么 Rust 不推断这个闭包的返回类型?

ios - Alamofire 无法将文件上传到 Google Cloud Storage

ios - 自定义 UIViewController 中的选择器不调用类中的函数

Swift 数据到带有数据 :content/type 的 base64 字符串

c# - 将泛型与 LINQ 继承结合使用