Swift中的NSMethodSignature怎么了?

jopen 10年前發布 | 12K 次閱讀 Swift Apple Swift開發

譯者注:翻譯自蘋果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

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!