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