Swift中的通用完成处理程序

5

我有一个方法,它有一个名为performRequest()的方法。 它需要一个JSONRequest参数。 JSONRequest大概长这样:

public typealias JSONCompletionHandler = ([Entity]?, NSError?) -> Void

public class JSONRequest: Request {
    public var completionHandler: JSONCompletionHandler
    public var endPoint: String
}

performRequest() 代码如下:

public func performJSONRequest<T where T: Entity>(jsonRequest: JSONRequest, _: Type) {
        // Make a request which returns a data object
        var entities = self.convertJSONData(data, jsonKey: jsonRequest.jsonKey, T.self)
        // Error: 'T' is not identical to 'Entity'
        jsonRequest.completionHandler(entities, error)
}

正如你所看到的,它调用了convertJSONData()函数,其代码如下:

func convertJSONData<T where T: Entity>(jsonData: AnyObject, _: T.Type) -> [T] {
        // Convert the data into Swift collection classes, enumerate over them, and create model objects
        var json = JSON(data: jsonData as NSData, options: nil, error: nil)
        var entities = [T]()

        for obj in json {
            let book = T(json: obj)
            entities.append(book)
        }

    return entities

Entity 是一个协议,我所有的模型类(例如 AuthorBook)都要遵守这个协议。

它定义了一个方法:init(json: JSON)。因为 T 被定义为 T:Entity,我可以调用 T:(json: obj) 来创建任何符合 Entity 协议的类的实例。

我希望能够使用 performJSONRequest() 方法来请求符合 Entity 协议的任何对象。例如,我想像这样构建一个针对书籍实例的请求:

var request = JSONRequest(endPoint: "books") { (let object: [Entity]?, let error: NSError?) -> Void in
    // Cast object to [Book] and have fun
}

performJSONRequest<Book>(request)

我真的想不出如何实现这个。目前,我在performJSONRequest()方法中得到一个错误,提示'T'与'Entity'不相同。如果我在完成处理程序中将数组定义为[AnyObject],我会得到相同的错误:'T'与'AnyObject'不相同

感谢任何帮助!

1个回答

15
解决方案是将通用类型提升到JSONRequest类中 - 这样JSONCompletionHandler就可以使用您请求的通用类型而不仅仅是Entity协议。 (您的一些代码似乎有点伪造,因此可能需要进行一些调整以适合您的实现。) JSONRequest现在是一个带有Entity类型约束的泛型类:
public class JSONRequest<T: Entity>: Request {
    // completion handler defined in terms of `T`
    public typealias JSONCompletionHandler = ([T]?, NSError?) -> Void

    // no further changes        
    public var completionHandler: JSONCompletionHandler
    public var endPoint: String
    public init(endPoint: String, completionHandler: JSONCompletionHandler) {
        self.endPoint = endPoint
        self.completionHandler = completionHandler
    }
}

performJSONRequest不再需要将类型作为单独的参数传递。由于jsonRequest已经专门化,它可以从该参数中获取类型信息:

public func performJSONRequest<T: Entity>(jsonRequest: JSONRequest<T>) {
    // create array of `T` somehow 
    var entities: [T] = []
    var error: NSError?

    // completionHandler expects [T]? and NSError?
    jsonRequest.completionHandler(entities, error)
}

在创建您的 JSONRequest 实例时,完成处理程序中给定的类型(例如,[Book]?)将设置通用 JSONRequest 的类型,并在整个过程中保持不变:
var request = JSONRequest(endPoint: "books") { (books: [Book]?, error) in
    println(books?.count)
}
performJSONRequest(request)

太棒了!我没有意识到在JSONRequest内定义JSONCompletionHandler会允许它使用其T类型占位符。非常感谢您的完整答案! - wander

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接