Swift中的NSMethodSignature怎么了?

译者注:翻译自苹果Swift官方博客 What Happened to NSMethodSignature?

screen-shot-2014-07-11-at-10-43-11-am

让Cocoa框架支持Swift语言的工作,给了我们一个全新的机会来审视里面的众多API。我们发现大多数不适合Swift编码风格的类,基本都是优先考虑安全性的。比如,一些关系到动态方法调用的类在Swift中没有暴露出来,它们NSInvocationNSMethodSignature这两个类。

我们最近收到一个bug报告,是从一个注意到这个现象的开发者发来的。他曾经使用Objective-C中的NSMethodSignature来在运行时检查方法参数的类型,但在迁移到Swift的过程中发现NSMethodSignature方法失效了。实际分析一下,被迁移的代码能够接收不同签名的HTTP处理程序,比如:

func handleRequest(request: HTTPRequest, queryStringArguments: [String: String]) { }
func handleRequest(request: HTTPRequest, jsonBody: JSON) { }

在Objective-C里,NSMethodSignature能被用来检测API参数类型,第一个方法的API必须接收一个[String: String]类型的参数,第二个方法需要接收一个JSON类型。然而,Swift足够强大,能够简单的处理这种状况而无需动用NSMethodSignature,并且在某种程度上,还能降低对编译器提供的类型提示和内存安全的破坏。

下面是在Swift用另一种方法来解决这个问题的代码:

struct HTTPRequest {
    // ...
}

protocol HTTPHandlerType {
    typealias Data

    /// :returns: true if the request was handled; false otherwise
    func handle(request: HTTPRequest, data: Data) -> Bool
}

首先,我们使用协议来定义一个接口,任何想要处理我们的HTTPRequest的程序都必须通过这个借口。这个协议非常简单,里面只包含一种方法。

这里为什么使用协议而不是HTTPHandler的子类呢?因为协议更灵活,能够将实现的细节交给客户端去做。如果使用HTTPHandler子类,我们需要让客户端同样使用它,并且强制客户端使用相同的引用类型。然而使用协议的话,客户端能够自行决定在代码中使用适合的类型,无论它们是类、结构体甚至是枚举类型。

class HTTPServer {
    func addHandler<T: HTTPHandlerType>(handler: T) {
        handlers.append { (request: HTTPRequest, args: Any) -> Bool in
            if let typedArgs = args as? T.Data {
                return handler.handle(request, data: typedArgs)
            }
            return false
        }
    }

    // ...
}

然后,我们的HTTPServer类拥有一个泛型方法,它接收一个HTTPHandlerType类型作为参数。使用处理程序的关联类型,它能执行args参数的条件式向下转换(conditional downcast),来检测这个处理程序是否应该处理该http请求。现在我们能看到定义HTTPHandlerType作为协议的好处了,HTTPServer不需要知道处理程序如何响应请求,甚至不需要知道处理程序本身,它只需要知道能够处理请求的值。

class HTTPServer {
    // ...

    private var handlers: [(HTTPRequest, Any) -> Bool] = []

    func dispatch(req: HTTPRequest, args: Any) -> Bool {
        for handler in handlers {
            if handler(req, args) {
                return true
            }
        }
        return false
    }
}

当我们的HTTPServer接收一个请求时,它将遍历一遍里面的处理程序,看是否有程序能响应这个请求。

现在我们能很简单的创建一个自定义的包含不同的参数类型的HTTPHandlerType,并且将它注册到HTTPServer

class MyHandler : HTTPHandlerType {
    func handle(request: HTTPRequest, data: Int) -> Bool {
        return data > 5
    }
}

let server = HTTPServer()
server.addHandler(MyHandler())
server.dispatch(HTTPRequest(...), args: "x") // returns false
server.dispatch(HTTPRequest(...), args: 5)   // returns false
server.dispatch(HTTPRequest(...), args: 10)  // returns true

通过协议和泛型的结合,现在我们能优雅的编写Swift代码来创建和注册包含不同类型的HTTP处理程序。这个方法还能够让编译器在保证运行时性能的同时确保类型安全。

EOF
  • 文章信息
  • 评论交流
2014年12月15日 3:15 发布。字数:2316
本文被收录于下面的话题: