Swift中的NSMethodSignature怎么了?
譯者注:翻譯自蘋果Swift官方博客 What Happened to NSMethodSignature?
讓Cocoa框架支持Swift語言的工作,給了我們一個全新的機會來審視里面的眾多API。我們發現大多數不適合Swift編碼風格的類,基本都是優先考慮安全性的。比如,一些關系到動態方法調用的類在Swift中沒有暴露出來,它們 NSInvocation 和 NSMethodSignature 這兩個類。
我們最近收到一個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