Swift中的NSMethodSignature怎么了?

jopen 10年前

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

b7vanri.png

让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处理程序。这个方法还能够让编译器在保证运行时性能的同时确保类型安全。

原文  http://idlelife.org/archives/910