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

时间:2014-06-19 14:24:21

标签: 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作为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?
}

6 个答案:

答案 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课程:

Swift 3.x

let myClass: AnyClass = type(of: self)

Swift 2.x

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)

雨燕5

情况不完全相同,但是我遇到了类似的问题。最终帮助我的是:

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
    }
    
}

别忘了使用边界而不是帧,因为在调用之前转换了点。