小编典典

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

swift

我有一个通用函数,该函数调用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所述,我尝试使用,但现在得到了:

错误:“类型’CityInfo.Type’不符合协议’AnyObject’”

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

class CityInfo : NSObject {

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

阅读 507

收藏
2020-07-07

共1个答案

小编典典

您用错误的方式进行处理:在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 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为编译器提供一种确定通用参数的方法。

2020-07-07